如何在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
相关产品推荐
相关产品推荐

