requestanimationframe的时间戳是浏览器自动传入的高精度绝对时间(毫秒),需用delta=currenttime-lasttime计算帧间隔,再乘以速度得到亚像素位移;应基于starttime而非上帧状态计算目标位置,避免卡顿导致瞬移,并注意清理rafid防止多实例冲突。

requestAnimationFrame 的时间戳参数怎么用
它不是可选的,而是浏览器自动传入的高精度时间戳(单位毫秒),代表当前帧开始渲染的时刻。这个值比 Date.now() 更准,且不会受系统时间调整影响。
常见错误是忽略它、用 performance.now() 重复取值,或误以为它是“从动画开始起经过的时间”——其实它是绝对时间,必须和上一帧时间做差才能得到真实帧间隔。
- 第一帧调用时,
raf传入的时间戳就是起点,应存为lastTime - 后续每帧都用
delta = currentTime - lastTime算出本次渲染距上次的真实耗时(单位毫秒) - 别直接用
currentTime做位移计算,否则动画速度会随页面后台/节流而漂移
怎么算匀速位移的像素增量
匀速 = 单位时间内移动固定距离。比如目标是「每秒移动 200px」,那每毫秒就该走 200 / 1000 = 0.2 px。再乘以本次帧间隔 delta,就得到这一帧该推进多少像素。
关键点在于:位移量必须和 delta 成正比,而不是写死每帧 +1px 或依赖帧率假设。
- 设目标速度为
speedPxPerMs = targetSpeedPxPerSec / 1000 - 每帧位移 =
speedPxPerMs * delta,累加到当前位置 - 用
element.style.transform = translateX(${x}px)更新,比改left性能更好 - 如果位移量太小(如
delta极短),累计值可能还没到 1px,但不用四舍五入——CSS 支持亚像素渲染,强行取整反而抖动
如何避免 requestAnimationFrame 积累误差或跳帧
手动维护 lastTime 并逐帧累加 delta,看似简单,但页面切后台、调试暂停、GC 卡顿都会导致某次 delta 异常大(几百毫秒),直接按它算位移就会“瞬移”。
正确做法是把动画逻辑和时间解耦:只记录「本该走到哪」,不依赖上一帧状态。
- 记录动画启动时的
startTime = performance.now() - 每帧用
elapsed = currentTime - startTime算总耗时 - 目标位置 =
startX + speedPxPerMs * elapsed(完全不依赖上一帧) - 这样即使某帧卡住 500ms,下一帧也能准确补上该走的距离,视觉上仍是匀速
- 记得在到达终点后调用
cancelAnimationFrame,否则持续运行浪费资源
为什么不用 CSS transition 或 scroll-driven animation
因为它们无法动态响应运行时变化的速度、方向或中断重定向。比如鼠标拖拽中实时调整目标位置,或根据传感器数据持续更新位移速率——这些都需要 JS 控制每一帧的精确输出。
requestAnimationFrame 提供了唯一能在浏览器渲染流水线中对齐的 JS 执行时机,配合时间戳就能做到设备无关的匀速控制。
真正容易被忽略的是:你得自己处理时间归零、暂停恢复、以及当用户快速反复触发动画时的 state 冲突。比如两次 start() 调用之间没清理前一个 rafId,就会同时跑多个动画循环。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











