如何在Three.js/编辑器中创建、引用动画及通过脚本调用?
嘿,刚好我在Three.js里折腾动画有段时间了,给你梳理下从本地创建动画、导入外部(Maya/Blender)动画,再到脚本控制的全流程,应该能精准命中你的核心需求~
一、先搞懂Three.js动画的核心基础
咱得先明确Three.js里的两种主流动画类型:
- 关键帧动画:适合给单个物体的属性(位置、旋转、缩放)做简单动画,比如让立方体旋转、移动
- 骨骼动画:适合角色、复杂模型的变形动画,比如人物走路、表情变化,依赖
SkinnedMesh(带骨骼的网格)和AnimationMixer(动画混合器)
不管是自己创建还是导入外部动画,最终都是通过AnimationMixer和AnimationAction来控制播放的,这俩是核心工具。
二、用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也支持直接创建骨骼,但一般更推荐用外部工具做(后面讲),这里只提核心逻辑:
- 创建
Bone和Skeleton绑定到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




