window.requestAnimationFrame() 方法告訴瀏覽器您希望執行動畫,并請求瀏覽器調用指定的函數在下一次重繪之前更新動畫。該方法將在重繪之前調用的回調作為參數。如果你想做逐幀動畫的時候,你應該用這個方法。這就要求你的動畫函數執行會先于瀏覽器重繪動作。通常來說,被調用的頻率是每秒60次,但是一般會遵循W3C標準規定的頻率。如果是后臺標簽頁面,重繪頻率則會大大降低。
瀏覽器的顯示頻率一般是16.7ms,我們將setTimeout的間隔時間設為16.7ms,節奏會如下圖第一行展示 。但如果我們將間隔時間設為10ms,那么節奏會如下圖第二行展示。
frame-lost.png
舉個例子:
國慶北京高速,最多每16.7s通過一輛車,結果,突然插入一批setTimeout的軍車,強行要10s通過。顯然,這是超負荷的,要想順利進行,只能讓第三輛車直接消失(正如顯示繪制第三幀的丟失)。然而,這是不現實的,于是就有了會堵車!
同樣的,顯示器16.7ms刷新間隔之前發生了其他繪制請求(setTimeout),導致所有第三幀丟失,繼而導致動畫斷續顯示(堵車的感覺),這就是過度繪制帶來的問題。不僅如此,這種計時器頻率的降低也會對電池使用壽命造成負面影響,并會降低其他應用的性能。
這也是為何setTimeout的定時器值推薦最小使用16.7ms的原因(16.7 = 1000 / 60, 即每秒60幀)。
而requestAnimationFrame就是為了解決這個問題而出現的。它所做的事情很簡單,就是跟著瀏覽器的繪制走,如果瀏覽設備繪制間隔是16.7ms,那它就在這個間隔繪制。如果瀏覽設備繪制間隔是10ms,它就10ms繪制。這樣就不會存在過度繪制的問題,動畫不會掉幀,播放自然流暢。
兼容性:
2017-04-02_112747.jpg
統一的向下兼容方法:
(function() {
var lastTime = 0;
var vendors = ['webkit', 'moz'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || // Webkit中此取消方法的名字變了
window[vendors[x] + 'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16.7 - (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);
};
}
}());