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

如何在JavaScript中实现无平滑Lerp以保持动画恒定速度?

实现Canvas图形恒定速度移动的正确方式

问题根源

你之前用的lerp写法是指数缓动插值:每帧移动当前到目标距离的固定比例(比如1%),所以图形越接近目标速度越慢,本质是平滑趋近效果,并非匀速。

后来加deltaTime的版本出错,一是因为没初始化lastFrameTimestamp,第一次计算的deltaTime会是无效值;二是误解了lerp参数的作用——固定比例乘以时间并不能实现匀速,反而会因为比例过小或过大导致异常。

正确实现方案(推荐直接计算位移)

要实现恒定速度,核心是每帧移动的距离 = 设定的速度 × 帧间隔时间(deltaTime),同时要处理到达目标后的停止逻辑。

完整代码示例

// 初始化变量
let currentPositionY = 0;
const destinationY = 500; // 目标位置
const moveSpeed = 100; // 每秒移动100像素(可根据需求调整)
let lastFrameTimestamp: number | null = null;

const onFrame = (timestamp: number) => {
  // 初始化第一帧的时间戳
  if (!lastFrameTimestamp) {
    lastFrameTimestamp = timestamp;
    window.requestAnimationFrame(onFrame);
    return;
  }

  // 计算帧间隔时间(转换为秒)
  const deltaTime = (timestamp - lastFrameTimestamp) / 1000;
  lastFrameTimestamp = timestamp;

  // 计算当前到目标的剩余距离
  const remainingDistance = destinationY - currentPositionY;
  // 计算本帧应该移动的距离
  const frameMoveDistance = moveSpeed * deltaTime;

  // 判断是否已经到达或超过目标位置
  if (Math.abs(frameMoveDistance) >= Math.abs(remainingDistance)) {
    currentPositionY = destinationY;
    // 到达后可选择停止动画:取消requestAnimationFrame
    // return;
  } else {
    // 根据方向移动(保证正负方向都正确)
    currentPositionY += remainingDistance > 0 ? frameMoveDistance : -frameMoveDistance;
  }

  // 这里写Canvas绘制逻辑
  // 比如:
  // const ctx = canvas.getContext('2d');
  // ctx.clearRect(0, 0, canvas.width, canvas.height);
  // ctx.fillRect(50, currentPositionY, 50, 50);

  window.requestAnimationFrame(onFrame);
};

// 启动动画
window.requestAnimationFrame(onFrame);

如果你一定要用lerp实现匀速

可以动态计算lerp的插值比例,让每帧的位移保持恒定:

const lerp = (start: number, end: number, amt: number) => (1 - amt) * start + amt * end;

let currentPositionY = 0;
const destinationY = 500;
const moveSpeed = 100;
let lastFrameTimestamp: number | null = null;

const onFrame = (timestamp: number) => {
  if (!lastFrameTimestamp) {
    lastFrameTimestamp = timestamp;
    window.requestAnimationFrame(onFrame);
    return;
  }

  const deltaTime = (timestamp - lastFrameTimestamp) / 1000;
  lastFrameTimestamp = timestamp;

  const remainingDistance = destinationY - currentPositionY;
  const frameMoveDistance = moveSpeed * deltaTime;

  if (Math.abs(frameMoveDistance) >= Math.abs(remainingDistance)) {
    currentPositionY = destinationY;
  } else {
    // 动态计算插值比例,保证移动距离恒定
    const amt = frameMoveDistance / remainingDistance;
    currentPositionY = lerp(currentPositionY, destinationY, amt);
  }

  // 绘制逻辑...
  window.requestAnimationFrame(onFrame);
};

window.requestAnimationFrame(onFrame);

关键注意点

  • 用requestAnimationFrame回调传入的timestamp计算deltaTime,比Date.now()更准确,避免系统时间偏差。
  • 必须处理到达目标后的逻辑,防止图形在目标位置来回跳动。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 01:01:51