深入理解 requestAnimationFrame

  • 2020 年 11 月 30 日
  • 筆記

深入理解 requestAnimationFrame

Web应用中,实现动画效果的方法如下:

  • JavaScript中可以通过setTimeout来实现
  • CSS3中可以通过transition和animation来实现
  • html5的canvas也可以实现
  • html5新增的请求动画API,requestAnimationFrame请求帧动画

requestAnimationFrame 背后的原理

屏幕刷新频率

图像在屏幕上的更新速度,也即屏幕上的图像每秒钟出现的次数,它的单位是赫兹(Hz)。
一般电子产品是60HZ

显示器有两种 : CRT和LCD

CRT是一种使用阴极射线管的显示器,屏幕上的图形图像是由一个个因电子束击打而发光的荧光点组成,由于显像管内荧光粉受到电子束击打后发光的时间很短,所以电子束必须不断击打荧光粉使其持续发光。电子束每秒击打荧光粉的次数就是屏幕刷新频率。

而对于LCD来说,则不存在刷新频率的问题,它根本就不需要刷新。因为LCD中每个像素都在持续不断地发光,直到不发光的电压改变并被送到控制器中,所以LCD不会有电子束击打荧光粉而引起的闪烁现象.

一秒钟刷新60次

1s = 1000ms;

1000ms/60 ≈ 16.7ms/帧

动画的原理

动画的本质 :让人眼看到图像被刷新而引起变化的视觉效果,这个变化要以连贯的、平滑的方式进行过渡

setTimeout

setTimeout 其实就是通过设置一个间隔时间来不断的改变图像的位置,从而达到动画效果的。但我们会发现,利用seTimeout实现的动画在某些低端机上会出现卡顿、抖动的现象。 这种现象的产生有两个原因 :

  • setTimeout的执行时间并不是确定的。在Javascript中, setTimeout任务被放进了异步队列中,只有当主线程上的任务执行完以后,才会去检查该队列里的任务是否需要开始执行,因此setTimeout的实际执行时间一般要比其设定的时间晚一些。

  • 刷新频率受屏幕分辨率和屏幕尺寸的影响,因此不同设备的屏幕刷新频率可能会不同,而 setTimeout只能设置一个固定的时间间隔,这个时间不一定和屏幕的刷新时间相同。

导致的结果 : setTimeout的执行步调和屏幕的刷新步调不一致,从而引起丢帧现象

毫秒数 屏幕刷新时间(16.7ms) setTimeout延迟时间(10ms)假设
第0ms 屏幕未刷新 setTimeout未启动
第10ms 屏幕未刷新 setTimeout开始执行 (left =1)
第16.7m 屏幕开始刷新(left = 1) setTimeout未执行 (left =1)
第20ms 屏幕未刷新(left = 1) setTimeout开始执行 (left =2)
第30ms 屏幕未刷新(left = 1) setTimeout开始执行(left = 3);
第33.4ms 屏幕开始刷新(left = 2) setTimeout未执行 (left =3);

我们可以看出屏幕并没有更新left = 2的那一帧动画,图像直接从1px跳到3px,丢帧的现象就会导致卡顿

requestAnimationFrame

requestAnimationFrame最大的优势是由系统来决定回调函数的执行时机,也就是会随着客户端显示器的的刷新率变化而变化

requestAnimationFrame 优势

CPU节能:使用setTimeout实现的动画,当页面被隐藏或最小化时,setTimeout 仍然在后台执行动画任务,由于此时页面处于不可见或不可用状态,刷新动画是没有意义的,完全是浪费CPU资源。而requestAnimationFrame则完全不同,当页面处理未激活的状态下,该页面的屏幕刷新任务也会被系统暂停,因此跟着系统步伐走的requestAnimationFrame也会停止渲染,当页面被激活时,动画就从上次停留的地方继续执行,有效节省了CPU开销。

函数节流:在高频率事件(resize,scroll等)中,为了防止在一个刷新间隔内发生多次函数执行,使用requestAnimationFrame可保证每个刷新间隔内,函数只被执行一次,这样既能保证流畅性,也能更好的节省函数执行的开销。一个刷新间隔内函数执行多次时没有意义的,因为显示器每16.7ms刷新一次,多次绘制并不会在屏幕上体现出来。