定时器 requestAnimationFrame

requestAnimationFrame 是什么?
它是一个浏览器提供的 API,用于在浏览器重绘之前调用指定的回调函数。它可以让浏览器在重绘之前更新动画/视觉效果,从而实现平滑的动画效果。

使用场景:requestAnimationFrame 经常用来构建平滑的动画效果和视觉效果。比如页面滚动,CSS3 过渡和动画,canvas 动画等。

原理:requestAnimationFrame 将回调函数加入浏览器的回调队列中,在下一次重绘之前调用。一般来说,屏幕刷新频率是 60Hz,也就是说每秒重新绘制 60 次。requestAnimationFrame 试图将重绘与此刷新频率同步,从而实现平滑的动画效果。

与 setTimeout/setInterval 的比较:
requestAnimationFrame 与 setTimeout/setInterval 都可以实现定时执行代码的功能,但 requestAnimationFrame 有更好的性能表现。这是因为:

  • requestAnimationFrame 与屏幕刷新同步,只在需要重绘的时候执行回调,而 setTimeout 是按照固定时间间隔执行。
  • requestAnimationFrame 会把多个回调集中在一起执行,减少重绘次数,而 setTimeout 会每个时间间隔就重绘一次。
  • requestAnimationFrame 可以保证平滑的动画效果,而 setTimeout 可能由于间隔时间过长产生卡顿感。
    所以,在不需要严格的时间间隔要求和需要平滑动画的场景下,requestAnimationFrame 是更好的选择。

不同浏览器的实现差异:
不同浏览器的刷新频率可能不同,实现方式也可能略有差异,这可能会导致动画效果在不同浏览器中有细微的差别。
在我的电脑上,屏幕的刷新率是 60Hz,它每一次的刷新频率大概是 16.666ms。

优点:

  1. 与屏幕刷新频率同步,实现平滑的动画效果;
  2. 节省 CPU、GPU 和电量;
  3. 避免布局抖动等视觉差异。

缺点:

  1. 回调函数的调用频率受屏幕刷新频率的限制,在低刷新频率下会感觉到卡顿;
  2. 若连续调用 requestAnimationFrame,可能会导致过高的 CPU 占用,应避免请求动画帧的频率超过屏幕刷新频率;
  3. 部分低端机型的刷新频率较低,动画效果可能不太平滑。

使用示例:

1
2
3
4
5
function animate() {
// 执行动画绘制等逻辑
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);

可以使用 cancelAnimationFrame 取消一个 requestAnimationFrame 的回调函数。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
let rafId;
function animate() {
// 执行动画绘制等逻辑
rafId = requestAnimationFrame(animate);
}

function startAnimation() {
rafId = requestAnimationFrame(animate);
}
// 取消 requestAnimationFrame
function stopAnimation() {
cancelAnimationFrame(rafId);
}