You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Three.js/编辑器中创建、引用动画及通过脚本调用?

嘿,刚好我在Three.js里折腾动画有段时间了,给你梳理下从本地创建动画、导入外部(Maya/Blender)动画,再到脚本控制的全流程,应该能精准命中你的核心需求~

一、先搞懂Three.js动画的核心基础

咱得先明确Three.js里的两种主流动画类型:

  • 关键帧动画:适合给单个物体的属性(位置、旋转、缩放)做简单动画,比如让立方体旋转、移动
  • 骨骼动画:适合角色、复杂模型的变形动画,比如人物走路、表情变化,依赖SkinnedMesh(带骨骼的网格)和AnimationMixer(动画混合器)

不管是自己创建还是导入外部动画,最终都是通过AnimationMixerAnimationAction来控制播放的,这俩是核心工具。

二、用Three.js原生创建动画

1. 关键帧动画(简单属性动画)

举个例子,给立方体做旋转+缩放的动画:

// 先创建立方体和场景基础
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

const cube = new THREE.Mesh(
  new THREE.BoxGeometry(1,1,1),
  new THREE.MeshStandardMaterial({color: 0xff0000})
);
scene.add(cube);
camera.position.z = 5;

// 1. 创建关键帧轨道:分别定义旋转和缩放的时间-值对应关系
const rotationTrack = new THREE.KeyframeTrack(
  '.rotation[y]', // 目标属性的路径(这里是立方体的Y轴旋转)
  [0, 2, 4], // 时间点(单位:秒)
  [0, Math.PI, Math.PI * 2] // 对应时间点的属性值
);

const scaleTrack = new THREE.KeyframeTrack(
  '.scale', // 缩放属性(xyz三个值)
  [0, 2, 4],
  [1,1,1,  2,2,2,  1,1,1] // 0秒时正常大小,2秒放大2倍,4秒回到原大小
);

// 2. 把轨道组合成一个动画剪辑(clip)
const clip = new THREE.AnimationClip('cubeAnim', 4, [rotationTrack, scaleTrack]);
// 参数:剪辑名称、总时长、轨道数组

// 3. 创建动画混合器和动作(action)
const mixer = new THREE.AnimationMixer(cube);
const action = mixer.clipAction(clip);
action.play(); // 启动动画

// 4. 必须在渲染循环里更新混合器!
const clock = new THREE.Clock();
function animate() {
  requestAnimationFrame(animate);
  const deltaTime = clock.getDelta(); // 获取两次渲染的时间差
  mixer.update(deltaTime); // 更新动画进度
  renderer.render(scene, camera);
}
animate();

2. 骨骼动画(复杂模型)

如果要做角色这类骨骼动画,Three.js也支持直接创建骨骼,但一般更推荐用外部工具做(后面讲),这里只提核心逻辑:

  • 创建BoneSkeleton绑定到SkinnedMesh
  • 给骨骼创建关键帧轨道,再通过AnimationMixer播放
三、导入Maya/Blender制作的外部动画

这是日常开发最常用的场景,咱重点讲:

1. 导出注意事项

  • 格式选GLB/GLTF:Three.js对这俩格式的支持最完善,动画、材质、骨骼都能完美导出
  • Blender导出步骤:选中要导出的模型 → 点击「文件-导出-GLB/GLTF」→ 勾选「Animation」选项 → 确保骨骼和皮肤权重都被正确导出
  • Maya导出步骤:用Maya的GLTF导出插件(官方或第三方),同样勾选动画相关选项,导出GLB格式

2. 导入并播放动画

GLTFLoader加载模型,然后提取动画剪辑播放:

import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
camera.position.z = 10;

const loader = new GLTFLoader();
// 替换成你的模型路径
loader.load('models/animated-character.glb', function(gltf) {
  const model = gltf.scene;
  scene.add(model);

  // 获取模型自带的动画剪辑数组
  const animations = gltf.animations;
  if (animations.length === 0) {
    console.warn('模型没有绑定动画哦');
    return;
  }

  // 创建动画混合器(绑定到模型根节点)
  const mixer = new THREE.AnimationMixer(model);
  
  // 播放第一个动画(比如 idle 待机动画)
  const idleAction = mixer.clipAction(animations[0]);
  idleAction.play();

  // 把混合器存到全局,方便后续控制
  window.characterMixer = mixer;
});

// 渲染循环更新动画
const clock = new THREE.Clock();
function animate() {
  requestAnimationFrame(animate);
  const deltaTime = clock.getDelta();
  if (window.characterMixer) {
    window.characterMixer.update(deltaTime);
  }
  renderer.render(scene, camera);
}
animate();
四、脚本控制动画的核心方法(你的核心需求)

这部分是重点,掌握这些就能完全掌控动画的播放逻辑:

1. 基础播放控制

// 假设已经有action对象(从mixer.clipAction获取)
action.play(); // 播放动画
action.pause(); // 暂停动画
action.reset(); // 重置到初始状态(回到第一帧)
action.stop(); // 停止动画并重置

2. 速度与方向控制

action.setSpeed(2); // 2倍速播放
action.setSpeed(0.5); // 0.5倍速慢放
action.setSpeed(-1); // 倒放动画

3. 循环模式设置

Three.js提供三种循环模式:

// 重复播放n次(这里是3次)
action.setLoop(THREE.LoopRepeat, 3);
// 来回播放(比如从第一帧到最后一帧,再倒回第一帧)
action.setLoop(THREE.LoopPingPong);
// 只播放一次
action.setLoop(THREE.LoopOnce);

4. 动画切换与淡入淡出

如果模型有多个动画(比如idle、walk、run),可以平滑切换:

// 假设已经获取了idleAction和walkAction
// 从idle切换到walk,0.5秒内淡入淡出,避免生硬跳转
idleAction.crossFadeTo(walkAction, 0.5);
walkAction.play();

5. 获取动画状态

console.log(action.isRunning()); // 动画是否在播放
console.log(action.time); // 当前播放到第几秒
console.log(action.getClip().duration); // 动画总时长
一些实用小技巧
  • 把多个动画动作存在一个对象里,方便管理:const anims = { idle: idleAction, walk: walkAction }
  • 如果骨骼动画播放异常,检查模型导出时是否勾选了「骨骼」和「皮肤权重」,或者模型的骨骼层级是否正确
  • 多个模型的动画可以共用一个AnimationMixer吗?不建议,每个模型最好单独用一个,避免冲突

内容的提问来源于stack exchange,提问作者shikitos

火山引擎 最新活动