在web应用中,实现动画效果的方法比较多,例如js中可以通过定时器setTimeout来实现;CSS3可以通过transition和animation来实现;html5中通过Canvas来实现。除此之外,html5还提供一个专门用于请求动画的API,即requestAnimationFrame(请求动画帧)。
屏幕绘制频率:
即图像在屏幕上更新的速度,即屏幕上图像每秒出现的次数,一般这个频率是60HZ,所以一幅画面的刷新频率大约是16.7ms。大多数浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,以为即使超过那个频率用户的体验也不会提升。
CSS动画原理
动画的本质就是让人眼看到图像被绘制而引起变化的视觉效果,这个变化要连贯、平滑的方式进行过渡。因为电脑屏幕以每秒60次的频率绘制,绘制频率很高所以感觉不到它在绘制。
js中使用setTimeout实现动画在某些低端机上会出现卡顿、抖动的现象,主要有两个原因:
(1)setTimeout的执行时间并不是确定的。在js中setTimeout任务被放入异步队列中,只有当主线程上的任务执行完之后,才会去检查异步任务是否需要开始执行,所以setTimeout的执行时间一般比设定的晚一些。
(2)刷新频率受屏幕分辨率和屏幕尺寸影响,不同设备的屏幕绘制频率可能会不同,而setTimeout只能设置一个固定的时间间隔,这个时间不一定和屏幕的刷新时间相同。
以上两种情况都会导致setTimeout的执行步调和屏幕的刷新步调不一致,从而引起丢帧现象。所以为什么步调不一致会出现丢帧呢?
首先setTimeout的执行只是在内存中对元素的属性进行改变,这个变化必须要等到屏幕下次绘制时才会被更新到屏幕上。如果步调不一致会导致中间某些帧的操作直接被跨越过去,而直接更新下一帧。
假设屏幕每隔16.7ms刷新一次,而setTimeout 每隔10ms设置图像向左移动1px, 就会出现如下绘制过程(表格)
从上面的绘制过程中可以看出,屏幕没有更新 left=2px 的那一帧画面,元素直接从left=1px 的位置跳到了 left=3px 的的位置,这就是丢帧现象,这种现象就会引起动画卡顿。
requestAnimationFrame(rAF)
与setTimeout相比,rAF的最大优势是由系统来决定回调函数的执行时机。具体来讲就是,系统每次绘制之前都会主动调用rAF中的回调函数。所以rAF的执行频率是跟着系统的绘制频率走的,他能保证回调函数在屏幕每一次的绘制间隔中只被执行一次,这样就不会出现丢帧现象和动画卡顿。
requestAnimationFrame()方法接收一个参数,即在绘制屏幕前调用的一个函数,这个函数负责改变下一次绘制的DOM样式。
function updateProcess(){ var div=document.getElementById('status'); div.style.width=(parseInt(div.style.width)+5)+'px'; if(div.style.width!="300px"){ window.requestAnimationFrame(updateProcess); } } window.requestAnimationFrame(updateProcess);
rAF还有两个优势:
(1)CPU节能:使用setTimeout实现的动画,当页面被隐藏或最小化时,setTimeout仍然在后台执行动画任务,由于此时页面处于不可见或不可用的状态,刷新动画是没有意义的,还浪费CPU资源。而rAF不同,当页面处于未激活的状态下,该页面的屏幕绘制任务也被系统暂停,因此跟着系统步伐的rAF也停止渲染,当页面被激活时,动画就从上次停留的地方继续执行,有效节约了CPU开销。
(2)函数节流:在高频率事件(resize、scroll等)这种,为了防止在一个刷新间隔内发生多次函数执行,使用rAF可保证每个绘制间隔内函数只被执行一次,这样既能保证流畅性,也能更好的节省函数执行的开销。