Effect:
Break down Threejs rendering principles
To render objects in Threejs, you need to have three basic elements:
- Scene Scene
- The Camera Camera
- The Renderer the Renderer
The scene can be understood as the world stage, the camera is your eye, and the renderer is understood as the brain, used to represent what your eye sees from the world stage. Before we start coding, there’s one more problem we need to solve. How is the boundary determined? Since we are going to animate, we need to determine the width and height of the boundary and reset the heart beyond the boundary to achieve good performance and reuse the heart.
Calculate the boundary
PerspectiveCamera(FOV, aspect, near, far) from Threejs.PerspectiveCamera(FOV, aspect, near, far)
Parameter Description:
- Fov: Angle of view, which can be interpreted as the Angle at which the eyes are opened, generally 45 to 60 is best
- Aspect: Aspect ratio
- Near: indicates the near end point
- Far: indicates the far endpoint
The near and far end can be understood as when you take a camera to shoot, the distance between you and the camera lens is the near end, and the distance between you and the shooting object is the far end. According to the perspective, two isosceles triangles are nested with each other. Height is the height of our page and the base of the smaller triangle is the rendered height.
That is, in Threejs Scene, the distance is calculated by the base of the small triangle, so we need to find the base of the small triangle.
Derivation process: Let the base of a small triangle be x according to the Pythagorean theorem:
tan(fov/2) = x/2/near
tan(fov/2) = height/2/far
That is:
x/near = height/far => x = height/far*near
So you can figure out the Scene boundary in Threejs
And because the default camera in Threejs is in the center position, that is, the coordinates will have positive and negative numbers, in order to facilitate the batch generation of love, we move the camera to the upper left corner and redefine the coordinate axis.
initScene(){
// Page width and height
let w = this.$q.screen.width
let h = this.$q.screen.height
// Define near and far endpoints
let near = 40
let far = 1000
// Calculate the width and height after rendering
let mw = w*near/far
let mh = h*near/far
this.mw = mw
this.mh = mh
// Define Threejs
this.scene = new THREE.Scene()
this.camera = new THREE.PerspectiveCamera(45,w/h,near,far)
// Set the camera position and redefine the axis
this.camera.position.set(mw/2,-mh/2,near)
this.renderer = new THREE.WebGL1Renderer()
this.renderer.setSize(w,h)
this.$refs.bgCanvas.appendChild(this.renderer.domElement)
this.drawScene()
}
Copy the code
Batch generation of love
Before writing the code, let’s take a look at Threejs’s Sprite system: A Sprite is a flat surface that always faces the camera, usually with a translucent texture. Sprites don’t cast any shadows
The Sprite can be generated by setting the image and by using the above operation, we know the render width MW, so we can iterate to generate the heart:
// Generate thirty hearts
for (let i = 0; i < 30; i++) {
// Read images to generate sprites
new THREE.TextureLoader().load( "love.png".(image) = >{
const material = new THREE.SpriteMaterial( { map: image } )
const sprite = new THREE.Sprite( material )
// Set the Sprite zoom to achieve the size of the heart
let zoom = randomNum(0.1.0.5)
sprite.scale.set(zoom,zoom,zoom)
// Randomly generate hearts according to render width MW
let x = randomNum(0.this.mw)
sprite.position.set(x,0.5.0)
this.scene.add( sprite )
})
}
Copy the code
Heart Drop animation
In Threejs, we have a requestAnimationFrame function that provides us with an animation, which is typically refreshed 60 times a second, or 60 frames. We could certainly use setInterval, but requestAnimationFrame has many advantages. Perhaps most importantly, it pauses when the user switches to another TAB, so it doesn’t waste valuable processor resources or drain battery life. So, I encapsulated it as a function to make each heart an animation on its own:
drawScene(){
for (let i = 0; i < 30; i++) {
new THREE.TextureLoader().load( "love.png".(image) = >{
const material = new THREE.SpriteMaterial( { map: image } )
const sprite = new THREE.Sprite( material )
let zoom = randomNum(0.1.0.5)
sprite.scale.set(zoom,zoom,zoom)
let x = randomNum(0.this.mw)
sprite.position.set(x,0.5.0)
this.scene.add( sprite )
const animeScene = () = >{
// Animation system
requestAnimationFrame(animeScene)
// Reset the render height if the render height is exceeded
if (sprite.position.y<-this.mh){
sprite.position.y = 0.5
}
// Depending on the zoom size, there are different falling speeds
sprite.position.y-=0.2-sprite.scale.x*0.1
this.renderer.render(this.scene,this.camera)
}
animeScene()
})
}
}
Copy the code
Demo site
MyLove