Leecason / blog

https://leecason.github.io
1 stars 0 forks source link

setTimeout,setInterval 与 requestAnimationFrame #6

Open Leecason opened 5 years ago

Leecason commented 5 years ago

参考

Leecason commented 5 years ago

setTimeoutsetInterval 不同,requestAnimationFrame 不需要设置时间间隔。

大多数电脑显示器的刷新频率是60Hz,大概相当于每秒钟重绘60次。大多数浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超过那个频率用户体验也不会有提升。因此,最平滑动画的最佳循环间隔是1000ms/60,约等于16.6ms。

setTimeoutsetInterval 的问题是,它们都不精确。它们的内在运行机制决定了时间间隔参数实际上只是指定了把动画代码添加到浏览器UI线程队列中以等待执行的时间。如果队列前面已经加入了其他任务,那动画代码就要等前面的任务完成后再执行。

requestAnimationFrame 采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销;也不会因为间隔时间太长,使用动画卡顿不流畅,让各种网页动画效果能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果。

CSS的 transition 和 animation 效果优于 JavaScript 实现的动画效果,原因在于CSS知道动画什么时候开始,会计算出正确的循环间隔,在适合的时候刷新UI。基于该问题,浏览器为了JavaScript动画添加了一个新API,即requestAnimationFrame

requestAnimationFrame 基本思想是利用显示器的刷新机制,与刷新频率保持同步,并利用这个刷新频率进行页面重绘更新。不过需要注意:因为JavaScript单线程工作机制,如果主线程一直处于繁忙状态,那么requestAnimationFrame的动画效果也会受影响的。

IE9-浏览器不支持 requestAnimationFrame,可以使用setTimeout来兼容。

Leecason commented 5 years ago

polyfill

// 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);
        };
}());