requestanimationframe 能匹配刷新率是因为它在浏览器每次提交新帧前执行,严格对齐 vsync 信号,自动适配不同屏幕刷新率;而 settimeout 执行时机不可控,易导致跳帧和性能问题。

requestAnimationFrame 为什么能匹配刷新率
它不是“每16ms调一次”,而是浏览器在每次准备提交新帧前,主动把所有注册的 requestAnimationFrame 回调集中执行——这个时机严格对齐显示器的 vsync 信号。60Hz 屏幕下约 16.7ms 一帧,120Hz 下自动缩为约 8.3ms,无需你手动适配。
而 setTimeout(fn, 16) 只是往宏任务队列塞函数,执行时间完全取决于主线程是否空闲。JS 正在解析大 JSON、处理滚动事件、或页面在后台时,回调可能严重延迟甚至堆积,直接导致跳帧。
- 页面切到后台标签页时,
requestAnimationFrame自动暂停;setTimeout仍会执行(现代浏览器虽节流至 1fps,但仍是冗余计算) -
requestAnimationFrame回调总在样式计算之前运行,确保 DOM 变更能被合并进同一帧,避免重复 layout/paint - 多个
requestAnimationFrame调用会被浏览器自动合并到同一帧,不会像for循环里连写 10 次那样瞬间注册一堆回调
不传 currentTime 参数就等于放弃帧率无关性
回调函数第一个参数 currentTime 是 DOMHighResTimeStamp,精度远高于 Date.now(),且与浏览器内部渲染时钟同源。丢掉它,你就只能靠赌设备刚好跑 60fps。
常见错误写法:this.x += 2 → 在 30fps 设备上位移只剩一半,在 120fps 上又快得离谱;正确做法是用时间差算真实位移:
let lastTime = performance.now();
function animate(currentTime) {
const deltaTime = currentTime - lastTime;
lastTime = currentTime;
this.x += (100 / 1000) * deltaTime; // 每秒移动 100px
requestAnimationFrame(animate);
}
- 首次
currentTime可能为 0,所以lastTime初始化推荐用performance.now(),而非 0 - 别用
startTime算总耗时做线性插值(如(timestamp - startTime) / duration),除非动画有明确起止时间;持续运动必须依赖deltaTime
动画循环里最隐蔽的卡顿源头:读-写-读 DOM
在 requestAnimationFrame 回调中反复读取 offsetTop、getBoundingClientRect() 或连续写 style.left 和 style.top,会触发强制同步布局(forced layout),打断渲染流水线,单帧内多次回流直接拖垮性能。
- 所有 DOM 读操作尽量前置并缓存(如提前记下
element.clientWidth) - 所有样式写操作尽量后置并批量(优先用
transform+opacity,它们走合成层,不触发布局) - 绝对避免在动画帧中调用
getComputedStyle、scrollIntoView、focus()等隐式触发 layout 的方法 - Chrome DevTools Performance 面板里看到大量紫色 Layout 块?基本就是这里出的问题
cancelAnimationFrame 不只是“停止”,更是资源守门员
漏掉 cancelAnimationFrame 不仅让动画停不下来,还会持续占用主线程、阻止 GC 回收相关闭包,尤其在组件卸载、路由切换时极易引发内存泄漏。
- 必须用变量保存上一次
requestAnimationFrame的返回值(一个数字 ID) - 退出条件判断要放在回调开头或结尾,且必须显式调用
cancelAnimationFrame(id) - 不要在循环里反复调
requestAnimationFrame,真正需要的是“单次注册 → 执行 → 判断 → 再注册(可选)”的链式推进 - 暂停/恢复动画时,不能只靠布尔开关,必须配合
cancelAnimationFrame和新requestAnimationFrame调用,否则旧回调仍在排队
真正卡顿的从来不是 API 本身,而是你在每一帧里偷偷干的那些事:读了不该读的布局、写了不该写的属性、忘了清理 ID、或者根本没用时间戳算真实流逝。这些细节不处理,换什么 API 都一样卡。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











