传统的 JavaScript 动画是通过 setTimeout 或者 setInterval 实现的,但是定时器动画一直存在两个问题:

  • 动画的循环间隔不好控制,设置长了动画显得不够平滑流畅,设置短了浏览器的重绘频率会达到瓶颈,推荐的最佳循环间隔是 17ms(大多数电脑的显示器刷新频率是 60Hz,1000ms/60)
  • 定时器第二个参数只是指定了多久后将动画任务添加到浏览器的 UI 线程队列中,如果 UI 线程处于忙碌状态,那么动画不会立刻执行。

为了解决这些问题,H5 中加入了 requestAnimationFramerequestIdleCallback

requestAnimationFrame 会把每一帧中的所有 DOM 操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率

在隐藏或不可见的元素中,requestAnimationFrame 将不会进行重绘或回流,这当然意味着更少的 CPU、GPU 和内存使用量

requestAnimationFrame 是浏览器专门为动画提供的 API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下,动画会自动暂停,有效节省了 CPU 开销

# 性能对比

requestAnimationFrame 的回调会在每一帧确定运行,属于高优先级任务;而 requestIdleCallback 的回调则不一定,属于低优先级。

我们看到的网页都是浏览器一帧一帧绘制出来的,通常认为 FPS 为 60 是比较流畅的,而 FPS 为个位数的时候用户就会感受到明显的卡顿。

在每一帧里,浏览器都处理了一下的事情:

包含了用户的交互、js 的执行、以及 requestAnimationFrame 的调用,布局计算以及页面的重绘等工作。

假如某一帧里面要执行的任务不多,在不到 16ms(1000/60)的时间内就完成了上述任务的话,那么这一阵就会有一定的空闲时间,这段时间就恰好可以用来执行 requestIdleCallback 的回调: