requestAnimationFrame动画
页面动画效果通常是使用CSS3的animation
,transition
,或者在JavaScript中使用setTimeout | setInterval
来持续改变某个CSS属性的值来实现。window还有一个API,用来实现高性能的帧动画,那就是requestAnimationFrame
。
requestAnimationFrame
语法
window.requestAnimationFrame(callback);
callback
是一个函数参数,在下一个可用的屏幕重绘中调用。可以使用window.cancelAnimationFrame()
来取消回调。回调函数会被传入DOMHighResTimeStamp参数,指示函数触发的时间,单位为毫秒。
栗子:
setInterval和requestAnimationFrame实现帧动画时的区别
前者用户指定刷新频率,但是由于用户系统资源等,这个延迟间隔可能不一致,就表现的不那么流畅;requestAnimationFrame
刷新频率和浏览器有关,通常是60次每秒,大多数浏览器会匹配w3c的建议频率。跟随浏览器的刷新频率执行回调,动画效果更好。
如果浏览器打开多个标签,requestAnimationFrame在非激活的页面中或者浏览器最小化时,动画会暂停或者减速,可以节省用户资源。
setTimeout和setInterval不断对屏幕更改,在屏幕实际能显示更改前强制执行不必要的回流,可能会造成更多的资源消耗,移动端可能更严重,电量消耗更多。
浏览器兼容性
现代浏览器基本都支持,只是一些低版本的需要添加前缀;IE是从10开始支持。可以对支持的浏览器使用requestAnimationFrame,不支持的浏览器使用setTimeout兼容:
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
// MIT license
(function() {
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame']
|| window[vendors[x] + 'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}
}());
延伸
动画和帧率
帧率FPS是每秒动画更新次数,值越高越流畅,大多数屏幕刷新频率为每秒60次,所以一般帧率达到60动画效果比较好。帧率比较低或者不稳定,动画会表现的卡顿。
可以使用chrome dev tool查看浏览器帧率:
JS动画 vs CSS动画
CSS动画的优势在于transition采用和requestAnimationFrame一样的机制,并且可以开启GPU加速(比如translate3d)。
另外Google Developer Chromium项目中:
CSS动画和原生支持的web动画Web Animations运行在浏览器的合成器线程(compositor thread),和主线程(main thread)不同,样式、排版布局、渲染和js的执行都在主线程上,这就意味着如果浏览器在主线程上执行一些消耗比较大的任务,这些动画也不会被打断。
如果任何动画触发了layout和paint,都会需要用主线程,这些开销会影响到相关的js和css的工作。关于哪些CSS属性会触发layout或者paint可以参考CSS Triggers。(transforms和opacity属性不会引起)
所以对基于Chromium的浏览器,在触发了文档layout或paint的情况下(比如更改top/left/width/height),CSS动画可能不会具有特别优于JS的效果,其它浏览器中也有类似的表现,有的甚至JS表现的更好。
参考: