傳統(tǒng)的JS動畫都是用 setTimeout 和 setInterval 實現(xiàn)的邑蒋,后來無意中在網(wǎng)上看到一個新的JS函數(shù) requestAnimationFrame 用它來替代傳統(tǒng)的JS動畫方法,說是效果更好,當(dāng)時也沒有仔細深究。直到昨天去魅族面試的時候庆锦,面試官問我有什么新的辦法可以替代傳統(tǒng)的JS動畫,我說“我知道一個叫 requestAnimationFrame 的函數(shù),它的執(zhí)行效果更好”携御。但是讓我仔細描述的時候,我就說不下去了既绕,這也是我寫這篇博客的初衷啄刹,我們學(xué)習(xí)的過程中一定要知其然比知其所以然,不要什么都略懂凄贩,最后落得跟半吊子一樣誓军。
定時器一直都是JS動畫的核心技術(shù)。而編寫動畫循環(huán)的關(guān)鍵是要知道延遲時間多長合適疲扎。一方面昵时,循環(huán)間隔必須足夠短,這樣才能讓不同的動畫效果顯得平滑流暢椒丧;另一方面壹甥,循環(huán)間隔還要足夠長,才能確保瀏覽器有能力渲染產(chǎn)生的變化壶熏。
大多數(shù)電腦顯示器的刷新頻率是60HZ句柠,也就是每秒鐘重繪60次。大多數(shù)瀏覽器都會對重繪操作加以限制,不超過顯示器的重繪頻率溯职,因為即使超過那個頻率用戶體驗也不會提升管怠。因此,最平滑動畫的最佳循環(huán)間隔是 1000ms / 60 缸榄,約為16.7ms渤弛。
傳統(tǒng)的 setTimeout 和 setInterval 它們都不是很精確,因為它們實際上只是把動畫代碼添加到瀏覽器UI線程隊尾以等待執(zhí)行時間甚带,如果它們前面有其它任務(wù)她肯,則必須等前面的任務(wù)執(zhí)行完在執(zhí)行動畫代碼。
而 requestAnimationFrame 采用系統(tǒng)時間間隔鹰贵,讓各種動畫效果能夠有一個統(tǒng)一的刷新機制晴氨,從而節(jié)省系統(tǒng)資源,提高系統(tǒng)性能碉输,改善視覺效果籽前。它有如下三個特點:
- 會把每一幀中所有的DOM操作集中起來,在一次動畫操作就完成敷钾,并且動畫的時間間隔緊緊跟隨瀏覽器的刷新頻率(不需要設(shè)置時間間隔)枝哄。
- 在隱藏或者不可見的元素中,不會進行動畫操作阻荒。
- 當(dāng)瀏覽器不是激活狀態(tài)挠锥,不會進行動畫操作。
下面是一個兼容所有瀏覽器的使用 requestAnimationFrame 的代碼(IE9-無該方法)
(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.7 - (currTime - lastTime));
var id = setTimeout(function() {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
}
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
}
}
}());
最后附上我利用 requestAnimationFrame 制作的一個 跳動的小球 的DEMO侨赡。