目前的Web页面中,实现动画的方式有很多,比如
- CSS3中的animation
- JS中的setTimeOut
- HTML5中的canvas
除此之外,HTML5中还提供了一个requestAnimationFrame,直译过来就是请求动画帧,弄懂这个东西之前,我们先来了解一下其他两个概念。
-
相关概念
-
1)屏幕刷新频率
现在的显示器主要有两种:LCD(Liquid Crystal Display )和CRT(Cathode Ray Tube),LCD是液晶显示屏,CRT传统显示器。
CRT显示器的原理是靠电子束激发屏幕内表面的荧光粉来显示图像的,由于荧光粉被点亮后很快会熄灭,所以电子枪必须循环地不断激发这些点。电子束每秒击打荧光粉的次数就是屏幕刷新频率。
LCD显示器的原理是液晶在不同电压的作用下会呈现不同的光特性,由许多个页面构成,一个液晶就是一个象素,而在彩色液晶显示屏中则每个象素由红绿蓝三个液晶共同构成,液晶是一直保持亮的,所以对于LCD来说不存在闪烁现象。
-
2)跳频丢帧
以上,对于CRT显示器来说,屏幕会以1000s/60Hz ≈ 16.7ms的频率不断刷新,当我们执行setTimeout(fn,10)时(10ms更新一次),就会出现跳频丢帧。举个例子,使用setTimeout每10ms向下移动1px:
- 第0ms:屏幕未刷新,setTimeout未执行
- 第10ms:屏幕未刷新,setTimeout开始执行并设置top=1px
- 第16.7ms:屏幕开始刷新,屏幕上的图像向下移动了1px, setTimeout 未执行
- 第20ms:屏幕未刷新,setTimeout开始执行并设置top=2px
- 第30ms:屏幕未刷新,setTimeout开始执行并设置top=3px
- 第33.4ms:屏幕开始刷新,屏幕上的图像向下移动了3px, setTimeout未执行
- …
可以看到bottom=2px的动画没有被屏幕刷新出来,就是就是丢帧。
-
刷新间隔
requestAnimationFrame的执行时机是由系统决定的,比如之前文中提到的60Hz,那就是≈16.7ms执行一次,如果是75Hz,那么这个时间间隔就变成了1000/75≈13.3ms,这样就可以避免丢帧现象,使用起来也非常简单。
var tops = 0;
var element = document.getElementById('SomeElementYouWantToAnimate');
element.style.position = 'absolute';
//回调函数
function render() {
tops += 1;
element.style.top = tops + 'px';
if (tops < 100) {
//递归渲染
window.requestAnimationFrame(render);
}
}
//第一帧渲染
window.requestAnimationFrame(render);
-
优点
- CPU节能:setTimeout无论再何时,只要开始了就会一直执行,除非手动终止,而requestAnimationFrame在最小化或者当前页面未激活的状态下是停止执行的,当激活时,会从上次执行停止的地方继续执行,节省了CPU的开销。
- 函数节流:在高频率事件(resize,scroll等)中,为了防止在一个刷新间隔内发生多次函数执行,使用requestAnimationFrame可保证每个刷新间隔内,函数只被执行一次,这样既能保证流畅性,也能更好的节省函数执行的开销。一个刷新间隔内函数执行多次时没有意义的,因为显示器每16.7ms刷新一次,多次绘制并不会在屏幕上体现出来,当然也可以自定义throttle方法,比如lodash._throttle()。
-
兼容性
requestAnimationFrame在不同浏览器的表现各不相同,主要体现在前缀不同,当然,有人做了polyfill,可以了解一下其实现方式:github代码。