前言
在研究canvas的2D pixi.js庫的時候,其動畫的刷新都用requestAnimationFrame替代了setTimeout 或 setInterval
但是jQuery中還是采用了setInterval,我這章就研究下順便改造下jQuery的動畫
定時器
jQuery動畫的實現考慮到兼容與易用性采用了setInterval來不斷繪制新的屬性值,從而達到動畫的效果。
大部分瀏覽器的顯示頻率是16.7ms,由於瀏覽器的特性,setInterval會有一個丟幀的問題
即使向其傳遞毫秒為單位的參數,它們也不能達到ms的准確性。這是因為javascript是單線程的,可能會發生阻塞
jQuery會有一個全局設置jQuery.fx.interval = 13
設置動畫每秒運行幀數。
默認是13毫秒。該屬性值越小,在速度較快的瀏覽器中(例如,Chrome),動畫執行的越流暢,但是會影響程序的性能並且占用更多的 CPU 資源
那么歸納一點最關鍵的問題:
開發着並不知道下一刻繪制動畫的最佳時機是什么時候
requestAnimationFrame
requestAnimationFrame 是專門為實現高性能的幀動畫而設計的一個API
說簡單點
- setInterval、setTimeout是開發者主動要求瀏覽器去繪制,但是由於種種問題,瀏覽器可能會漏掉部分命令
- requestAnimationFrame 就是瀏覽器什么要開始繪制了瀏覽器自己知道,通過requestAnimationFrame 告訴開發者,這樣就不會出現重復繪制丟失的問題了
目前已在多個瀏覽器得到了支持,包括IE10+,Firefox,Chrome,Safari,Opera等,在移動設備上,ios6以上版本以及IE mobile 10以上也支持requestAnimationFrame,
唯一比較遺憾的是目前安卓上的原生瀏覽器並不支持requestAnimationFrame,不過對requestAnimationFrame的支持應該是大勢所趨了,安卓版本的chrome 16+也是支持requestAnimationFrame的。
比較
區別:
- requestAnimationFrame 會把每一幀中的所有DOM操作集中起來,在一次重繪或回流中就完成,並且重繪或回流的時間間隔緊緊跟隨瀏覽器的刷新頻率,一般來說,這個頻率為每秒60幀
- 隱藏或不可見的元素中,requestAnimationFrame將不會進行重繪或回流,這當然就意味着更少的的cpu,gpu和內存使用量。
- requestAnimationFrame也會像setTimeout一樣有一個返回值ID用於取消,可以把它作為參數傳入cancelAnimationFrame函數來取消requestAnimationFrame的回調
兼容處理
摘自HTML5 Canvas 核心技術
window.requestNextAnimationFrame = (function () { var originalWebkitRequestAnimationFrame = undefined, wrapper = undefined, callback = undefined, geckoVersion = 0, userAgent = navigator.userAgent, index = 0, self = this; // Workaround for Chrome 10 bug where Chrome // does not pass the time to the animation function if (window.webkitRequestAnimationFrame) { // Define the wrapper wrapper = function (time) { if (time === undefined) { time = +new Date(); } self.callback(time); }; // Make the switch originalWebkitRequestAnimationFrame = window.webkitRequestAnimationFrame; window.webkitRequestAnimationFrame = function (callback, element) { self.callback = callback; // Browser calls the wrapper and wrapper calls the callback originalWebkitRequestAnimationFrame(wrapper, element); } } // Workaround for Gecko 2.0, which has a bug in // mozRequestAnimationFrame() that restricts animations // to 30-40 fps. if (window.mozRequestAnimationFrame) { // Check the Gecko version. Gecko is used by browsers // other than Firefox. Gecko 2.0 corresponds to // Firefox 4.0. index = userAgent.indexOf('rv:'); if (userAgent.indexOf('Gecko') != -1) { geckoVersion = userAgent.substr(index + 3, 3); if (geckoVersion === '2.0') { // Forces the return statement to fall through // to the setTimeout() function. window.mozRequestAnimationFrame = undefined; } } } return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback, element) { var start, finish; window.setTimeout(function () { start = +new Date(); callback(start); finish = +new Date(); self.timeout = 1000 / 60 - (finish - start); }, self.timeout); }; }()); window.cancelNextRequestAnimationFrame = window.cancelRequestAnimationFrame || window.webkitCancelAnimationFrame || window.webkitCancelRequestAnimationFrame || window.mozCancelRequestAnimationFrame || window.oCancelRequestAnimationFrame || window.msCancelRequestAnimationFrame || clearTimeout;
對比
通過requestAnimationFrame與setInterval同樣的效果對比
一段動畫采用setInterval與requestAnimationFrame,分別給出了webKit下調用刷新的次數
setInterval實現,調用了144次左右
requestAnimationFrame 實現調用了120次左右
通過對比,在同樣的時間里,通過定時器刷新的次數是要更多,當然定時器跟設置的時間是有關系的
這里我要強調一點,有效值,就是我們在瀏覽器能接受的范圍內,給出一個最佳的渲染時間,這樣才是一個性能優化的最佳的選擇