深入理解 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刷新一次,多次绘制并不会在屏幕上体现出来。