关于浏览器帧丢弃问题与requestAnimationFrame作用的技术问询
你的基础认知没有偏差:60Hz刷新率下,浏览器每帧的完整渲染预算约为16.67ms(1000ms/60),如果主线程被JS任务占用超过这个时长,就会错过当前帧的垂直同步(vsync)信号,导致该帧无法渲染,用户就会感知到卡顿。
问题1 滚动事件回调的卡顿分析
结论是:不会每次触发都恰好丢6帧,但一定会造成明显的可感知卡顿,实际丢帧数会略高于6帧。
- 首先要明确:滚动事件的触发频率没有和渲染帧对齐,主流浏览器会在两次渲染的间隔内触发46次`scroll`事件,当第一个`scroll`事件触发、100ms的`handleScroll`开始执行时,主线程会被完全占用,后续触发的所有`scroll`事件、渲染任务、其他交互任务都会进入队列等待。等100ms回调执行完成后,主线程还需要处理队列中积压的任务,这部分额外耗时也会占用帧预算,所以60Hz下实际丢帧数一般在79帧区间。
- 定性判断标准:只要滚动回调的执行时长超过30ms,普通用户就能感知到滚动粘滞感,超过100ms属于严重卡顿。
- 定量分析方法:可以用Chrome DevTools的Performance面板录制滚动操作过程,查看主线程的任务瀑布流,宽度超过16.67ms的黄色块就是长任务,数该长任务覆盖了多少个vsync时间节点(面板中会用灰色竖线标注),就能得到准确的丢帧数量。
问题2
requestAnimationFrame的防丢帧逻辑 你的理解完全正确:如果传入requestAnimationFrame的回调本身执行耗时超过16.67ms,依然会不可避免地丢帧。requestAnimationFrame的作用不是完全避免丢帧,而是消除所有因执行时机不对导致的不必要丢帧,它的价值体现在这两点:
- 执行时机固定:
requestAnimationFrame的回调会被浏览器统一安排在每帧渲染流程的最开始执行,你在回调中修改的样式、计算的动画状态,一定会被纳入当前帧的渲染pipeline,不会出现「用定时器/事件回调修改样式,结果刚好错过当前帧渲染时机,白白等到下一帧才更新」的无效丢帧。 - 天然和刷新率对齐:
requestAnimationFrame的执行频率完全匹配显示器的刷新率,每帧最多执行一次回调。如果用setTimeout/setInterval实现动画,定时器间隔和刷新率不匹配时,会出现部分帧执行两次动画逻辑、部分帧完全不执行的跳帧问题,requestAnimationFrame从根源上避免了这种情况。
简单来说:requestAnimationFrame解决的是「代码性时机错误导致的丢帧」,无法解决「代码本身性能不足导致的丢帧」,后者需要你通过优化回调耗时来解决。
内容的提问来源于stack exchange,提问作者Joji
相关产品推荐
相关产品推荐

