Skip to content

40. 补间动画库 Tween.js

Published:

前面我们做过一些动画:

比如相机位置的圆周运动:

2025-04-04 22.02.48.gif

这是改变 position.x、position.z

还有雪花下落的动画:

2025-04-03 18.33.08.gif

这是改变 position.y

我们实现动画的时候,都是在每一帧改变固定的值:

image.png

比如旋转角度固定 +0.03,然后算出 cos、sin

image.png

比如 y 值固定加一个值

这种动画自然都是匀速的。

但如果我们不想让它匀速,而是刚开始慢慢的加速,中间匀速,结束的时候慢慢的减速呢?

这种有个加速、减速的缓冲过程的,就叫做缓动动画。

我们自己算这种值的变速的改变肯定比较麻烦,所以一般都会用一个动画库,比如 tween.js

tween.js 叫补间动画库,就是指定开始、结束的数值,会补充中间数值的意思。

它的用法很简单,比如文档里的这个案例:

image.png

指定一个开始的值、结束的值,动画时间,变化规律,之后每帧渲染的时候调用下 update 就可以算出当前时间的值。

我们来用一下:

mkdir tween-animation
cd tween-animation
npm init -y

image.png

进入项目,创建 index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        body {
            margin: 0;
        }
    </style>
</head>
<body>
    <script type="importmap">
    {
        "imports": {
            "three": "https://esm.sh/three@0.174.0/build/three.module.js",
            "three/addons/": "https://esm.sh/three@0.174.0/examples/jsm/",
            "@tweenjs/tween.js": "https://esm.sh/@tweenjs/tween.js"
        }
    }
    </script>
    <script type="module" src="./index.js"></script>
</body>
</html>

这里要在 importmap 里加上 @tweenjs/tween.js 这个包

image.png

然后创建 index.js

import * as THREE from 'three';
import {
    OrbitControls
} from 'three/addons/controls/OrbitControls.js';
import mesh from './mesh.js';

const scene = new THREE.Scene();

scene.add(mesh);

const pointLight = new THREE.DirectionalLight(0xffffff);
pointLight.position.set(100, 300, 200);
scene.add(pointLight);

const axesHelper = new THREE.AxesHelper(200);
scene.add(axesHelper);

const width = window.innerWidth;
const height = window.innerHeight;

const camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000);
camera.position.set(200, 200, 200);
camera.lookAt(0, 0, 0);

const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height)

function render() {
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}

render();

document.body.append(renderer.domElement);

const controls = new OrbitControls(camera, renderer.domElement);

创建 Scene、Light、Camera、Renderer。

然后安装下 three 的类型包:

npm install --save-dev @types/three

创建 mesh.js

import * as THREE from 'three';

const geometry = new THREE.BoxGeometry(30, 30, 30);
const material = new THREE.MeshPhongMaterial({
    color: 'orange'
});

const mesh = new THREE.Mesh(geometry, material);

export default mesh;

画一个简单的立方体

看下效果:

npx live-server

image.png

image.png

如果我们想让它运动到 100、100、0 的位置,怎么做呢?

之前是这样的:

image.png

if(mesh.position.x < 100) {
    mesh.position.x += 1;
    mesh.position.y += 1;
}

每帧给 x、y 改变一个固定的值。

2025-04-05 17.56.50.gif

这样就是一个匀速运动。

或者用 clock.getDelta 基于每帧的时间间隔来运动:

image.png

const clock = new THREE.Clock();
function render() {
    const delta = clock.getDelta();

    if(mesh.position.x < 100) {
        mesh.position.x += delta * 30;
        mesh.position.y += delta * 30;
    }

    renderer.render(scene, camera);
    requestAnimationFrame(render);
}

2025-04-05 17.58.44.gif

如果想先加速再减速呢?

这时候就得用 tween.js

image.png

image.png

import {Tween, Easing} from '@tweenjs/tween.js';
const tween = new Tween(mesh.position)
    .to({ x: 100, y: 100}, 2000)
    .easing(Easing.Quadratic.InOut)
    .start();

function render(time) {
    tween.update(time);

    renderer.render(scene, camera);
    requestAnimationFrame(render);
}

我们只需要指定开始的数值、结束的数值,运动时间,然后指定用先加速再减速的动画 InOut 就可以了。

start 之后每次 render 调用下 update 更新下数值,它会根据时间算出现在应该是什么数值。

2025-04-05 18.06.26.gif

可以看到,它是有个先加速再减速的过程的。

这就是动画库 tween.js 的用法。

之前我们相机的圆周运动是自己写的:

image.png

现在可以改成用 Tween.js 来实现:

image.png

const r = 50; 
const tween = new Tween({ angle: 0 })
    .to({ angle: Math.PI * 2 }, 5000)
    .onUpdate(function(obj){
        camera.position.x = r * Math.cos(obj.angle);
        camera.position.z = r * Math.sin(obj.angle);

        camera.lookAt(0, 0, 0);
    }).start();

指定 angle 从 0 到 360 度变化,默认是匀速的,5s 转一圈。

onUpdate 的时候改变 camera 的 position.x position.y 调整 lookAt

这里把 camera 的初始 position.y 调小一点,离近观察:

image.png

看下效果:

2025-04-05 18.21.31.gif

可以看到相机匀速转了一圈,然后停下来了。

我们改成 InOut 的缓动效果,然后 repeat 无数次:

image.png

.easing(Easing.Quadratic.InOut)
.repeat(Infinity)

2025-04-05 18.24.20.gif

可以感觉出来有个加速、减速的缓冲过程,这就是缓动动画。

案例代码上传了小册仓库

总结

这节我们学了补间动画库 tween.js

它只要指定开始数值、结束数值、动画时间、缓动效果,就可以实现动画。

只要每一帧渲染的时候调用 update 传入当前时间就好了。

缓动动画是开始有段加速、结束的时候有段减速,类似这种有缓冲过程的动画,会看起来比较自然。

前面写动画效果都是自己改变数值的,以后我们都会用 tween.js 来改变数值。

评论