Hey guys, how are you!?
Today I'll talk about Three.js and how we can implement it with Angular! And after that, we'll create a simple 3D animation from Three.js!
So, let's start! First, let's see what Three.js is!
๐ฏ Three.js in a nutshell:
Three.js is an amazing javascript library, you can do amazing things on your website and make it powerful, beautiful! Three.js lets you work with 3D geometry, objects, lights, textures and some other cool stuff! And Three.js has three important things: scene, camera and renderer!
Take a look at these examples and let your mind wander through so many possibilities you can create!
Now, let's work ๐ฉ๐ฝโ๐จ ๐ง๐ฝโ๐จ!
Installing dependencies:
๐๐ป Install on the project
npm install three
๐๐ป Install on the project
npm i @types/three
Generating and importing the Canvas Box Component:
Let's create our canvas component, so with Angular CLI
we'll create our component:
๐๐ป Create the canvas component
ng g c components/canvas-box
And importing it in our app.module.ts
:
...
import { CanvasBoxComponent } from './components/canvas-box/canvas-box.component';
@NgModule({
imports: [
BrowserModule
],
declarations: [
AppComponent,
CanvasBoxComponent
]
})
...
Implementing and Understanding the Canvas Box structure:
First, in our canvas-box.component.html
we need to create the canvas html structure and add an id to our canvas, so the structure is like below:
<canvas id="canvas-box"></canvas>
Now, let's create the magic with three.js, in our canvas-box.component.ts
let's create a createThreeJsBox()
method, add in our ngOnInit()
Lifecycle hooks and import the Three.js :
...
import * as THREE from 'three';
export class CanvasBoxComponent implements OnInit {
ngOnInit(): void {
this.createThreeJsBox();
}
createThreeJsBox(): void {
}
}
...
We need to get our canvas element html, so let's create a const and get the element:
...
export class CanvasBoxComponent implements OnInit {
...
createThreeJsBox(): void {
const canvas = document.getElementById('canvas-box');
}
}
...
โ ๏ธ Important
document.getElementById
is not good to use with Angular because we have SSR! I only used it because in my opinion it is the simplest and most used way! If you want to use it, I recommend to make a validation of if it's SSR side and if it is, exit the function! Here I have an installable package that you can do this validation: ngx-verify-is-server-side.
In the next steps we are going to create our scene, material and two types of lights to place in our scene! So, let's create some const's and the code looks like below:
...
export class CanvasBoxComponent implements OnInit {
...
createThreeJsBox(): void {
const canvas = document.getElementById('canvas-box');
const scene = new THREE.Scene();
const material = new THREE.MeshToonMaterial();
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
const pointLight = new THREE.PointLight(0xffffff, 0.5);
pointLight.position.x = 2;
pointLight.position.y = 2;
pointLight.position.z = 2;
scene.add(pointLight);
}
}
๐จ๐ฝโ๐ซ๐ฉ๐ฝโ๐ซ Understanding the code:
const scene = new THREE.Scene()
Here we declare a THREE.Scene()
, the scene is necessary and important for us, because with it we can show our animation! The scene is like our "animation scene" and everything we create we need to add to our scene!
const material = new THREE.MeshToonMaterial()
We declare our material canvas, the material
"is like" the css of our 3D element! Three.js has a few types of stuff, see here
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
We declare a THREE.AmbientLight
in our scene, set the color and intensity of our light and add the ambientLight to our scene.
const pointLight = new THREE.PointLight(0xffffff, 0.5);
pointLight.position.x = 2;
pointLight.position.y = 2;
pointLight.position.z = 2;
scene.add(pointLight);
We create a new THREE.PointLight
declaring the color and intensity, change the position of the pointLight
and add it in to our scene
.
Now let's create our 3D geometries and add it to our scene, let's create a Box and a Torus Geometry and the code is like below:
...
export class CanvasBoxComponent implements OnInit {
...
createThreeJsBox(): void {
/*The our code is here, I removed it because of the total lines*/
...
const box = new THREE.Mesh(
new THREE.BoxGeometry(1.5, 1.5, 1.5),
material
);
const torus = new THREE.Mesh(
new THREE.TorusGeometry(5, 1.5, 16, 100),
material
);
scene.add(torus, box);
}
}
๐จ๐ฝโ๐ซ๐ฉ๐ฝโ๐ซ Understanding the code:
We create two 3D geometries by declaring the properties of each geometry, add material("css") to our geometry and then add the geometry to our scene.
In the next steps we will declare the screen sizes, create the camera and renderer! The code is as below:
...
export class CanvasBoxComponent implements OnInit {
...
createThreeJsBox(): void {
/*The our code is here, I removed it because of the total lines*/
...
const canvasSizes = {
width: window.innerWidth,
height: window.innerHeight,
};
const camera = new THREE.PerspectiveCamera(
75,
canvasSizes.width / canvasSizes.height,
0.001,
1000
);
camera.position.z = 30;
scene.add(camera);
if (!canvas) {
return;
}
const renderer = new THREE.WebGLRenderer({
canvas: canvas,
});
renderer.setClearColor(0xe232222, 1);
renderer.setSize(canvasSizes.width, canvasSizes.height);
}
}
๐จ๐ฝโ๐ซ๐ฉ๐ฝโ๐ซ Understanding the code:
const canvasSizes = {
width: window.innerWidth,
height: window.innerHeight,
};
Create an object with width
and height
properties based in the window.
const camera = new THREE.PerspectiveCamera(
75,
canvasSizes.width / canvasSizes.height,
0.001,
1000
);
camera.position.z = 30;
scene.add(camera);
We create our camera, the camera is like what the human eye sees. It is the most common projection mode used for rendering a 3D scene and declare the THREE.PerspectiveCamera
properties, de fov: 75
, aspect: canvasSizes.width / canvasSizes.height
, near: 0.001
and far: 1000
. And after that we change the camera.position.z
and add the camera to our scene.
We need to make our scene responsive, so we need to update the canvasSizes
object in the window resize event, so let's do that!
...
export class CanvasBoxComponent implements OnInit {
...
createThreeJsBox(): void {
/*The our code is here, I removed it because of the total lines*/
...
window.addEventListener('resize', () => {
canvasSizes.width = window.innerWidth;
canvasSizes.height = window.innerHeight;
camera.aspect = canvasSizes.width / canvasSizes.height;
camera.updateProjectionMatrix();
renderer.setSize(canvasSizes.width, canvasSizes.height);
renderer.render(scene, camera);
});
}
}
๐จ๐ฝโ๐ซ๐ฉ๐ฝโ๐ซ Understanding the code:
We added the resize listener and we updated some important properties, the canvasSizes
properties, the camera.aspect
, the camera.updateProjectionMatrix()
and the renderer
properties. And after that we render the updated scene
and camera
.
Now let's animate our 3D geometries and make the scene cooler! We'll use the THREE.Clock()
, getElapsedTime()
, update de X, Y, Z
properties of our geometries, update de renderer
and call the animation again on the next frame with requestAnimationFrame
, then the code is like below:
...
export class CanvasBoxComponent implements OnInit {
...
createThreeJsBox(): void {
/*The our code is here, I removed it because of the total lines*/
...
const clock = new THREE.Clock();
const animateGeometry = () => {
const elapsedTime = clock.getElapsedTime();
// Update animation objects
box.rotation.x = elapsedTime;
box.rotation.y = elapsedTime;
box.rotation.z = elapsedTime;
torus.rotation.x = -elapsedTime;
torus.rotation.y = -elapsedTime;
torus.rotation.z = -elapsedTime;
// Render
renderer.render(scene, camera);
// Call animateGeometry again on the next frame
window.requestAnimationFrame(animateGeometry);
};
animateGeometry();
}
}
๐จ๐ฝโ๐ซ๐ฉ๐ฝโ๐ซ Understanding the code:
const clock = new THREE.Clock();
Crete a const with THREE.Clock()
, it is for keeping track of time.
const elapsedTime = clock.getElapsedTime();
We declare a new const to get the seconds passed since the clock started and sets and to animate the "velocity" of our animation.
box.rotation.x = elapsedTime;
box.rotation.y = elapsedTime;
box.rotation.z = elapsedTime;
torus.rotation.x = -elapsedTime;
torus.rotation.y = -elapsedTime;
torus.rotation.z = -elapsedTime;
Here we animate our geometries changing the X, Y, Z
positions.
renderer.render(scene, camera);
We render the scene, camera updated.
window.requestAnimationFrame(animateGeometry);
We declare our animateGeometry
in requestAnimationFrame
to do the our animation.
animateGeometry();
Just calling our function :D
COOOLLL, we have a cooler Three.js implementation and after that we can make awesome geometries combinations! ๐๐๐๐๐ฅ ๐ฅ
Now we have a basic understanding of Three.js and its structure!
Three.js is awesome and we have countless possibilities of things to do with it and make our sites powerful.
Here we can see our study case live example ๐ค๐ป:
That's all guys, I hope you like it and if any questions, suggestions or other topics, please comment!
See you later โ๐ผ