动画混合
两条片段分别驱动上臂与小臂,两个权重滑杆决定各自的影响比例。
说明
机械臂由立柱、上臂与小臂组成,上臂的原点被安排在转动轴心上: 几何体先沿 X 平移半个长度,再把网格放到轴心位置, 这样改旋转就是绕轴转,不会整体甩出去。
两条片段都以四元数轨道驱动一个关节,时间轴同为 2.4 秒。
AnimationMixer 每帧把两条片段的结果按
setEffectiveWeight 混合,权重为 0 的那条不再影响姿态。
| 设置 | 数值 | 说明 |
|---|---|---|
| 片段时长 | 2.4 秒 | 两条片段共用同一组时间点 |
| 关键帧 | 5 个 | 0、0.6、1.2、1.8、2.4 秒 |
| 上臂摆幅 | ±0.62 弧度 | 约 ±35 度,绕 Z 轴 |
| 小臂摆幅 | −0.85 – 0.75 弧度 | 相位与上臂错开 |
| 默认权重 | 0.60 / 0.35 | 两者之和不必等于 1 |
| 时间步长上限 | 0.05 秒 | 切到后台再回来时不会瞬移 |
轨道怎么命名
关键帧轨道用「节点名.属性」定位目标。节点名取自
Object3D.name,在本例中分别是 upper 与 lower。
Mixer 的根节点是机械臂所在的组,它只在这一棵子树里查找,
不会误伤场景里的其他同名对象。
const trackUpper = new THREE.QuaternionKeyframeTrack(
'upper.quaternion',
[0, 0.6, 1.2, 1.8, 2.4],
quatValues([0, 0.62, 0, -0.62, 0])
);
const clipUpper = new THREE.AnimationClip('upper-swing', 2.4, [trackUpper]);
const mixer = new THREE.AnimationMixer(rig);
const actionA = mixer.clipAction(clipUpper);
actionA.play();
actionA.setEffectiveWeight(0.6);
旋转必须用四元数轨道。若写成欧拉角的三个数,
中间帧可能出现万向节死锁,姿态会以奇怪的角度穿过。
位置变化则用 VectorKeyframeTrack,缩放同理。
权重混合与交叉淡入
手动拖滑杆相当于直接设定权重,适合调试。
代码里更常见的写法是 crossFadeTo:
在指定时长内把当前动作淡出、目标动作淡入,两者的权重自动互补。
// 从走路切到跑步,1.2 秒内完成过渡
actionWalk.crossFadeTo(actionRun, 1.2, true);
第三个参数为 warp,开启后两条片段的时间轴会按比例缩放,
让脚步节奏平滑衔接。关闭时只混合姿态,可能出现瞬间的步频跳变。
每帧必须更新
mixer.update(dt) 是所有姿态变化的唯一来源,
渲染循环里漏掉它,画面会停在第一帧。
dt 建议设上限,页面被切到后台再切回来时
Clock 会返回一个很大的间隔,动画会直接跳过一整轮。
动画是姿态,不是数据。关键帧只描述节点的旋转与位移, 与要素属性、时间序列数据没有关系。 用动画表达时序变化时,需要自己把时间映射到关键帧位置。