1. Three.js 动画系统核心概念
- AnimationClip 一段命名的动画片段,包含多个 KeyframeTrack(关键帧轨道)。每个 Clip 对应一个动作,如 "walk"、"idle"、"jump"。
- KeyframeTrack 针对特定属性(position/rotation/scale/morphTargetInfluences)的关键帧序列,定义时间点和对应的值。
- AnimationMixer 动画播放器,绑定到某个对象(如角色根节点),负责播放、混合、过渡多个 AnimationClip。
- AnimationAction Mixer 播放某个 Clip 的实例,控制播放状态(play/pause/stop)、循环模式、权重、时间缩放。
2. AnimationMixer + AnimationClip
import * as THREE from 'three';
// ── 手动创建关键帧动画 ──
// 位置关键帧轨道:属性名 '.position',时间 [0, 1, 2],值 [x,y,z] * 3
const posTrack = new THREE.VectorKeyframeTrack(
'.position',
[0, 0.5, 1, 1.5, 2], // 关键帧时间点(秒)
[0,0,0, 0,2,0, 2,2,0, 2,0,0, 0,0,0] // 对应位置值(每3个一组)
);
// 旋转关键帧轨道(四元数)
const q0 = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, 0, 0));
const q1 = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, Math.PI, 0));
const rotTrack = new THREE.QuaternionKeyframeTrack(
'.quaternion',
[0, 1, 2],
[...q0.toArray(), ...q1.toArray(), ...q0.toArray()]
);
// 缩放关键帧
const scaleTrack = new THREE.VectorKeyframeTrack(
'.scale',
[0, 1, 2],
[1,1,1, 1.5,1.5,1.5, 1,1,1]
);
// 组合成 AnimationClip
const clip = new THREE.AnimationClip(
'bounce', // 动画名称
2, // 时长(秒),-1 = 自动从轨道推断
[posTrack, rotTrack, scaleTrack]
);
// 创建 Mixer 并播放
const mixer = new THREE.AnimationMixer(mesh);
const action = mixer.clipAction(clip);
action.loop = THREE.LoopRepeat; // LoopOnce / LoopRepeat / LoopPingPong
action.play();
// ⚠️ 动画循环中必须更新 mixer
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
mixer.update(delta); // 推进动画时间
renderer.render(scene, camera);
}
3. 骨骼动画 SkeletonHelper
骨骼动画用于角色/生物动画。骨骼(Bone)形成层级树,蒙皮(Skinned Mesh)的顶点跟随骨骼变形。通常从 GLTF 模型中加载,不需要手动创建:
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
const gltf = await new Promise((res, rej) =>
loader.load('/models/character.glb', res, undefined, rej)
);
const model = gltf.scene;
scene.add(model);
// 显示骨骼辅助器(调试用)
const skeletonHelper = new THREE.SkeletonHelper(model);
scene.add(skeletonHelper);
// 播放模型内置的动画
const mixer = new THREE.AnimationMixer(model);
console.log('可用动画:', gltf.animations.map(a => a.name));
// 播放第一个动画
const action = mixer.clipAction(gltf.animations[0]);
action.play();
4. GSAP 补间动画集成
GSAP(GreenSock Animation Platform)是 JavaScript 生态最强大的补间动画库,与 Three.js 完美配合:
# 安装 GSAP
npm install gsap
import gsap from 'gsap';
// 移动相机到新位置(2秒,ease 缓动)
gsap.to(camera.position, {
duration: 2,
x: 5, y: 3, z: 8,
ease: 'power2.inOut',
onUpdate: () => camera.lookAt(0, 0, 0)
});
// 序列动画(timeline)
const tl = gsap.timeline();
tl
.to(cube.position, { y: 2, duration: 0.5, ease: 'power2.out' })
.to(cube.rotation, { y: Math.PI * 2, duration: 1 }, '-=0.2')
.to(cube.position, { y: 0, duration: 0.5, ease: 'bounce.out' });
// 悬浮动画循环
gsap.to(mesh.position, {
y: '+=0.5',
duration: 1.5,
ease: 'sine.inOut',
yoyo: true, // 来回播放
repeat: -1 // 无限循环
});
// 颜色动画(需要特殊处理)
gsap.to(mesh.material.color, {
r: 1, g: 0.5, b: 0,
duration: 1
});
// 点击触发入场动画
function enterAnimation(object) {
object.scale.set(0, 0, 0);
gsap.to(object.scale, {
x: 1, y: 1, z: 1,
duration: 0.6,
ease: 'back.out(1.7)'
});
}
5. 实战:角色动画状态机(idle / walk / run)
class CharacterController {
constructor(model, animations) {
this.mixer = new THREE.AnimationMixer(model);
this.actions = {};
this.current = null;
// 加载所有动画 Clip
animations.forEach(clip => {
const action = this.mixer.clipAction(clip);
this.actions[clip.name] = action;
});
this.play('idle');
}
play(name, fadeDuration = 0.3) {
const next = this.actions[name];
if (!next || next === this.current) return;
if (this.current) {
// crossFadeTo:从当前动画淡出到新动画
this.current.crossFadeTo(next, fadeDuration, true);
}
next.reset().play();
this.current = next;
}
update(delta) {
this.mixer.update(delta);
}
}
// 使用
const character = new CharacterController(gltf.scene, gltf.animations);
// 根据速度切换动画
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
const speed = getCharacterSpeed(); // 0, 1-3, 3+
if (speed === 0) character.play('idle');
else if (speed < 3) character.play('walk');
else character.play('run');
character.update(delta);
renderer.render(scene, camera);
}
本章小结:Three.js 动画系统围绕 AnimationMixer 展开——它负责播放和混合 AnimationClip。来自 GLTF 模型的骨骼动画直接通过 mixer.clipAction() 播放;程序性动画用 GSAP 补间;复杂交互动画用状态机管理过渡。