【ThreeJS Basics 1-5】动画 Animations

208 阅读2分钟

Three JS 中的动画

了解如何生成动画,才能去创造出更丰富的场景,所以动画是很基本的知识!!首先需要了解一个JavaScript 的动画函数

window.requestAnimationFrame(fn)

window.requestAnimationFrame() 方法会告诉浏览器你希望执行一个动画。它要求浏览器在下一次重绘之前,调用用户提供的回调函数。

先来一组基础代码

基本代码

import * as THREE from 'three'
import gsap from 'gsap'

/**
 * Base
 */
// Canvas
const canvas = document.querySelector('canvas.webgl')

// Scene
const scene = new THREE.Scene()

/**
 * Base
 */
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 })
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)

/**
 * Sizes
 */
const sizes = {
    width: 800,
    height: 600
}

/**
 * Camera
 */
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height)
camera.position.z = 3
scene.add(camera)

/**
 * Renderer
 */
const renderer = new THREE.WebGLRenderer({
    canvas: canvas
})
renderer.setSize(sizes.width, sizes.height)

/**
 * Animate
 */
gsap.to(mesh.position, { duration: 1, delay: 1, x: 2 })

const tick = () =>
{
    // Render
    renderer.render(scene, camera)

    // Call tick again on the next frame
    window.requestAnimationFrame(tick)
}

tick()

此时运行项目,打开控制台就会看到一直再打印 tick

修改显示器刷新率的对比

我尝试修改显示器的刷新频率试一下,我从 170hz 调整到 60hz,可以看到控制台打印的速率明显变慢

请添加图片描述

以下是 60Hz 的样子,比起 170Hz,打印的速度慢了一些

在这里插入图片描述 说明 window.requestAnimationFrame(tick) 会根据刷新率的高低来处理回调函数的频率


基础的动画尝试

可以通过这个回调函数来试着做位移,旋转的动画

在这里插入图片描述 在这里插入图片描述

/**
 * Animate
 */
// gsap.to(mesh.position, { duration: 1, delay: 1, x: 2 })

const tick = () =>
{
    console.log('tick')

    // update objects
    mesh.rotation.x -= 0.01
    renderer.render(scene, camera)
    // Call tick again on the next frame
    window.requestAnimationFrame(tick)
}

tick()

不过,如果你的显示器帧率越高,那么这些动画的速度是越快的,因为在一秒钟内你有更多的 tick 函数的调用

不同帧率导致动画速率不同

我们来打印一下

/**
 * Animate
 */
// gsap.to(mesh.position, { duration: 1, delay: 1, x: 2 })

let time = Date.now()

const tick = () =>
{
    // console.log('tick')
    let currentTime = Date.now()
    let deltaTime = currentTime - time
    time = Date.now()

    console.log('deltaTime:>>', deltaTime)

    // update objects
    mesh.rotation.x -= 0.01
    renderer.render(scene, camera)
    // Call tick again on the next frame
    window.requestAnimationFrame(tick)
}

tick()

这个是 170Hz在这里插入图片描述

这个是 60Hz在这里插入图片描述 那么如何让不同刷新率的显示器,显示相同速度的动画呢?

解决方案一:DeltaTime


/**
 * Animate
 */
// gsap.to(mesh.position, { duration: 1, delay: 1, x: 2 })

let time = Date.now()

const tick = () =>
{
    // console.log('tick')
    let currentTime = Date.now()
    let deltaTime = currentTime - time
    time = Date.now()

    console.log('deltaTime:>>', deltaTime)

    // update objects
    mesh.rotation.x -= 0.001 * deltaTime
    renderer.render(scene, camera)
    // Call tick again on the next frame
    window.requestAnimationFrame(tick)
}

tick()

让我们来实验一下

在这里插入图片描述 在这里插入图片描述 看起来是有效果的!

解决方案2:Clock

// Clock
const clock = new THREE.Clock()

const tick = () =>
{
    const elapsedTime = clock.getElapsedTime()
    console.log('elapsedTime:>>', elapsedTime)

    // update objects
    mesh.rotation.x = elapsedTime * Math.PI * 0.5
    renderer.render(scene, camera)
    // Call tick again on the next frame
    window.requestAnimationFrame(tick)
}

tick()

这个是对比图

在这里插入图片描述

在这里插入图片描述 再来试试正弦函数

在这里插入图片描述

在这里插入图片描述

方法3: 动画库 Gsap

gsap 自己就会调用动画帧,所以不用告诉 gsap 自行更新

在这里插入图片描述 但是渲染的结果(也就是 Render) 还是要自己维护,所以render 要放到回调里。

如何选择方案?

  • 根据你的项目,你的熟练度
  • 以及项目体积,性能的权衡