You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

three.js项目如何实现关键帧之间的平滑插值过渡效果

three.js 关键帧动画平滑过渡实现方案

1. 核心原理

你当前的问题是只在时间完全匹配关键帧时才更新位置,缺少关键帧之间的插值计算。我们需要通过线性插值(Lerp)计算两个相邻关键帧之间任意时间点的位置,实现平滑过渡。

2. 第一步:预处理关键帧数据

首先把JSON里的对象结构转成按时间升序排列的关键帧数组,避免遍历顺序混乱的问题:

// 预处理右侧物体的位置关键帧
const positionKeyframes = Object.entries(json.right.position)
  .map(([timeStr, pos]) => ({
    time: parseFloat(timeStr),
    position: new THREE.Vector3(pos[0], pos[1], pos[2])
  }))
  .sort((a, b) => a.time - b.time); // 按时间从小到大排序

3. 第二步:每一帧计算当前插值位置

在你的渲染循环里,每次更新时按如下逻辑计算当前位置,替换原有直接匹配时间的逻辑:

function updateObjectPosition(obj, currentSeconds, keyframes) {
  const frameCount = keyframes.length;
  // 边界情况:时间小于第一个关键帧,直接用第一帧位置
  if (currentSeconds <= keyframes[0].time) {
    obj.position.copy(keyframes[0].position);
    return;
  }
  // 边界情况:时间大于最后一个关键帧,直接用最后一帧位置
  if (currentSeconds >= keyframes[frameCount - 1].time) {
    obj.position.copy(keyframes[frameCount - 1].position);
    return;
  }
  // 找到当前时间所在的前后两个关键帧
  let prevFrame = keyframes[0];
  let nextFrame = keyframes[frameCount - 1];
  for (let i = 0; i < frameCount - 1; i++) {
    if (currentSeconds >= keyframes[i].time && currentSeconds <= keyframes[i+1].time) {
      prevFrame = keyframes[i];
      nextFrame = keyframes[i+1];
      break;
    }
  }
  // 计算插值因子 范围0~1
  const alpha = (currentSeconds - prevFrame.time) / (nextFrame.time - prevFrame.time);
  // 线性插值计算当前位置
  obj.position.lerpVectors(prevFrame.position, nextFrame.position, alpha);
}

// 调用示例,放在你的渲染循环中执行
updateObjectPosition(obj, seconds, positionKeyframes);

4. 原有代码问题说明

你原有代码的判断条件存在逻辑错误:if(seconds === json.position.right[key]) 里 json.position.right[key] 是位置数组,不是时间值,应该匹配的是parseFloat(key)才对,这也是你之前只有刚好命中时间点才会更新位置的原因之一。

扩展优化

如果需要更自然的运动效果,可以把线性插值换成缓动函数,比如平方缓动、立方缓动,只需要把计算出来的alpha值传入缓动函数做一次转换即可。


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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 10:24:03