关于requestAnimationFrame()函数的理解

问题描述

问题来源于一个用canvas的getContext('2d')对象的小DEMO,里面用了requestAnimationFrame()这个API

在MDN里面,他解释为:
该方法告诉浏览器您希望执行动画并请求浏览器在下一次重绘之前调用指定的函数来更新动画,当你需要更新屏幕画面时就可以调用此方法。在浏览器下次重绘前执行回调函数。回调的次数通常是每秒60次,但大多数浏览器通常匹配 W3C 所建议的刷新频率。

下面有几点的问题
问题是:
1.这个函数可不可以理解为就是一个 1000/60 的定时器呢?
2.如果不是,那是不是相当于一个监听器,监听重绘这个事件然后执行吗?那么我之前写的每一帧都会触发这个函数吗?
3.重绘的理解。这里的重绘我理解是画面改变了,这样的话,比如我用canvas画了一个圆或者一个窗口大小的填充矩形,算重绘吗?

阅读 3.2k
1 个回答

要理解requestAnimationFrame,你得先要理解event loop事件循环,看下图
图片描述

简单解释下图片,事件循环就是进程会一直在中间转圈
当没有别的事情的时候,会在中间循环做自己的事
当接到通知有任务时,任务开关会打开,会进到任务列表里执行任务
当有渲染需求时,渲染开关会打开,进入渲染步骤
如此反复,整个过程是同步的

下面是解答

requestAnimationFrame如图所示,在渲染的前面,也就是说在requestAnimationFrame会在渲染之前执行好,每次的执行时间是在渲染之前,而进程是以同步的方式进行的,所以并没有办法确定时间就会是 1000/60 ,当进程在进入渲染之前有别的阻塞,那直到阻塞结束才会进到渲染里面,比如用了alert()或者任务列表里有长时间的计算.

使用

setTimeout比起来,requestAnimationFrame会在每次渲染之前执行,那么就可以把跟渲染相关的操作放在这里,比如像盒子的移动,要根据滚动条位置来计算盒子的位置等等,这样可以保持和渲染的频率同步,就是不会丢帧.纵享丝滑体验.
之前我也是这么认为的,只是理想很性感,现实很骨感.
最开始的时候我查了下requestAnimationFrame 兼容性,发现兼容性真心不错的,除了一些老古董都能支持的.
后来有发现的一个问题,支持了是不错,但并不是所有requestAnimationFrame都是在渲染之前的,有的是在渲染之后的,如下图
图片描述
这就尴尬了,如果只考虑chromefirefox的话,使用requestAnimationFrame体验会很好,如果要兼容更多就需要考虑更多的兼容方案了.

更多详情,可以参考第一个链接里的视频,讲的很生动

补充(回复评论)

我的意思是整个事件循环的过程是同步的,也就是说如果还在执行栈当中,接到任务通知,打开任务开关,在执行空闲的时间进到任务列表里面,这是同步,而不会直接跳到任务列表,渲染也是一样.
你说的微任务和宏任务任务的异步,是指他们具体的执行函数是异步的,但是去调用他们的过程是同步的.

比如下面这段代码

var a = 1

function setA(num) {
  a = num
  // while (true) {}  // while1
  console.log(a)
}
setTimeout(() => {
  setA(2)
}, 1000);

setTimeout(() => {
  console.log('setTimeout 1500')
}, 1500);
console.log(a)

// while (true) {} // while2

当执行到 setTimeout的时候,会往任务列表里面添加一条任务,这个时候并不会执行setTimeout的回调,会继续执行console.log(a),这个时候执行栈为空,当符合setTimeout的触发条件时,也就是到1000ms时,进程会进到任务列表去触发任务.

如果把后面一个while (true) {}代码注释去掉,那么永远也不会执行setTimeout里面的回调,因为进程一直在循环里面,不会去触发setTimeout的回调.

微任务和宏任务任务的异步,体现在当进程触发了回调函数之后,这个回调函数的执行不会去阻塞主进程,比如把第一个while (true) {}去掉,当进程去触发回调的时候,并不会因为回调里面的无限循环而阻塞,而是会继续往前执行.然后在1500ms的时候触发第二个任务,输出setTimeout 1500,而console.log(a)永远不会输出.

这里确实不对,是我太粗心了.

PS:

当然如果还是看不懂,那可能是我解释的不够清楚,或者受限于我的表达能力不够好,只能说声抱歉了,你可以看看参考的第一个链接里面的视频,解释的会比我清楚和生动很多.

或者可以另外在开一个关于 event loop的问题,我们慢慢交流,这个问题毕竟是用来讨论requestAnimationFrame

event loop的解释为了下面做解释时更加容易理解,如果无法理解event loop,那么就不好去理解requestAnimationFrame的作用,以及不同位置所产生的影响

参考

图片截取自 youtube Jake Archibald: In The Loop - JSConf.Asia 2018
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏