本文將會簡單講講 requestAnimationFrame 函數的用法,與 setTimeout/setInterval 的區別和聯系,以及當標簽頁隱藏時 requestAnimationFrame、setTimeout 各自的后續渲染。
requestAnimationFrame
說到 requestAnimationFrame,不得不提到 canvas 動畫,而說到 canvas 動畫,又不得不說到 setTimeout/setInterval。
canvas 動畫是基於逐幀重繪的,我們可以用 setTimeout 或者 setInterval 函數,設置定時器,每過一定時間,完成數據更新,繼而重繪到畫布上。但是這樣做有個缺點,可能會有卡頓。舉個簡單的例子,假設瀏覽器一秒刷新 10 次(正常的瀏覽器都在 60 fps 左右),重繪的時間點為 0ms,100ms,200ms,300ms .. 1000ms,用 setInterval 來做動畫,間隔設置為 60ms,也就是每 60ms 更新數據,那么數據更新點為 60ms,120ms,180ms,240ms ... ,100ms 的時候實際更新了一次數據,200ms的時候實際已經更新了三次數據,恩,理論上會出現不流暢的情況(如果沒有聽懂,可以參考下張鑫旭大神的文章 CSS3動畫那么強,requestAnimationFrame還有毛線用?有個很好的比喻)。what'more,setTimeout/setInterval 的時間間隔並不是像傳的參數那般准確,事實上,setTimeout 間隔通常會大於所傳的數,而 setInterval 可能會因為丟失回調而小於所定數字,具體可以看下 從setTimeout談JavaScript運行機制。
這時候,我們設想,如果瀏覽器在重繪前,通知客戶端數據更新,那就太棒了!requestAnimationFrame 函數閃亮登場!requestAnimationFrame 是瀏覽器用於定時循環操作的一個接口,類似於 setTimeout,主要用途是按幀對網頁進行重繪。設置這個 API 的目的是為了讓各種網頁動畫效果(DOM 動畫、Canvas 動畫、SVG 動畫、WebGL 動畫)能夠有一個統一的刷新機制,從而節省系統資源,提高系統性能,改善視覺效果。代碼中使用這個 API,就是告訴瀏覽器希望執行一個動畫,讓瀏覽器在下一個動畫幀安排一次網頁重繪。
使用方式非常簡單,現代瀏覽器已經支持無前綴的 API,低版本的現代瀏覽器可以用帶前綴的 API 回退,低版本 ie 可以用 setTimeout 回退(具體兼容性可以參考 http://caniuse.com/#search=requestAnimationFrame):
window.requestAnimFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000 / 60);
};
})();
一個簡單的使用:
// 調用 相當於setTimeout里的callback
function animationLoop(){
// logic
document.body.innerHTML = currentnumber++;
// 循環
requestID = requestAnimFrame(animationLoop);
}
// start
requestID = requestAnimFrame(animationLoop);
跟 setTimeout 一樣,RAF 也可以取消掉。非常簡單,以上面代碼為例,將 requestID 傳入 cancelAnimationFrame 函數即可:
window.cancelAnimationFrame(requestID);
事實上,我還是比較習慣用一個變量來表示是否繼續重繪:
// 調用 相當於setTimeout里的callback
function animationLoop(){
// logic
document.body.innerHTML = currentnumber++;
// 循環
if (flag) // 繼續重繪直到 flag === false
requestID = requestAnimFrame(animationLoop);
}
有一個完整的 polyfill,可以參考下谷歌大神的這篇文章 requestAnimationFrame for Smart Animating
標簽頁隱藏時
RAF 宣稱,使用這個 API,一旦頁面不處於瀏覽器的當前標簽,重繪頻率則會大大降低。大大降低到底是降低到什么程度?出於好奇,我決定進一步探索下。
做法非常簡單,將標簽頁隱藏,然后看看刷新頻率,輸出前后兩個刷新的間隔時間。
chrome,當標簽頁隱藏時,不再繼續刷新。firefox 中,間隔時間如下:
1017
1997
3998
7999
15993
32000
63994
127999
很明顯的指數型增長趨勢。
setTimeout/setInterval 呢?當標簽頁被隱藏時還是一樣工作嗎?經測試(僅在 firefox & chrome 下測試),當時間間隔大於 1000ms 時,如果標簽頁被隱藏,還是按照設定的時間執行回調;如果時間間隔小於 1000ms,當標簽頁被隱藏時,時間間隔會被延遲到 1000ms 執行回調。
Read More: