requestAnimationFrame动画

页面动画效果通常是使用CSS3的animationtransition,或者在JavaScript中使用setTimeout | setInterval来持续改变某个CSS属性的值来实现。window还有一个API,用来实现高性能的帧动画,那就是requestAnimationFrame

requestAnimationFrame

语法

window.requestAnimationFrame(callback);

callback是一个函数参数,在下一个可用的屏幕重绘中调用。可以使用window.cancelAnimationFrame()来取消回调。回调函数会被传入DOMHighResTimeStamp参数,指示函数触发的时间,单位为毫秒。

栗子:

See the Pen Jvbxax by NINE (@sollrei) on CodePen.


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表现的更好。

参考:
Animating with javascript: from setInterval to requestAnimationFrame
Animations and Performance
Myth Busting: CSS Animations vs. JavaScript

Comments
Write a Comment