你需要知道的requestAnimationFrame
隨著前端的發展,css已經能夠實現非常多的動畫特效,但是仍然存在css無法完成的動畫任務(比如頁面滾動),通常的解決方案都是使用js中的setInterval來設定定時器來實現動畫特效,比如下面的一個基本的動畫迴圈。
(function() {
function updateAnimations() {
doAnimation1();
doAnimation2();
}
setInterval(updateAnimations, 100);
})();
複製程式碼
該程式碼實現的功能是每隔100毫秒執行函式操作來達到動畫效果,然而,使用計時器真的可靠嗎? 答案當然是 no
由於JavaScript是單執行緒的,所以定時器的實現是在當前任務佇列完成後再執行定時器的回撥的,假如當前佇列任務執行時間大於定時器設定的延遲時間,那麼定時器就不是那麼可靠了,如下所示:
let startTime = new Date().getTime();
setTimeout(()=>{
let endTime = new Date().getTime();
console.log(endTime - startTime);
},50)
for(let i=0;i<20000;i++) {
console.log(1);
}
複製程式碼
輸出如下
可以看到,設定了50毫秒後執行,實際執行延遲時間遠大於這個數值,這就會導致動畫效果並不會達到想要的效果。
動畫是由瀏覽器按照一定的頻率一幀一幀的繪製的,由css實現的動畫的優勢就是瀏覽器知道動畫的開始及每一幀的迴圈間隔,能夠在恰當的時間重新整理UI,給使用者一種流暢的體驗,而setInterval或setTimeout實現的JavaScript動畫就沒有這麼可靠了,因為瀏覽器壓根就無法保證每一幀渲染的時間間隔,一般情況下,每秒平均重新整理次數能夠達到60幀,就能夠給人流暢的體驗,即每過 1000/60 毫秒渲染新一幀即可,但從上面的例子知,這一點單靠定時器是無法保證的。 為此,requestAnimationFrame應運而生,其作用就是讓瀏覽器流暢的執行動畫效果。可以將其理解為專門用來實現動畫效果的api,通過這個api,可以告訴瀏覽器某個JavaScript程式碼要執行動畫,瀏覽器收到通知後,則會執行這些程式碼的時候進行優化,實現流暢的效果,而不再需要開發人員煩心重新整理頻率的問題了。
使用方法如下:
function animationWidth() {
var div = document.getElementById('box');
div.style.width = parseInt(div.style.width) + 1 + 'px';
if(parseInt(div.style.width) < 200) {
requestAnimationFrame(animationWidth)
}
}
requestAnimationFrame(animationWidth);
複製程式碼
效果如下(GIF錄製的有點卡。。。實際效果請參考示例):
可以看到,requestAnimationFrame接受一個動畫執行函式作為引數,這個函式的作用是僅執行一幀動畫的渲染,並根據條件判斷是否結束,如果動畫沒有結束,則繼續呼叫requestAnimationFrame並將自身作為引數傳入。從示例來看,得到了效果平滑流暢的動畫,這樣就巧妙地避開了每一幀動畫渲染的時間間隔問題。
requestAnimationFrame的相容性參考caniuse
在高階瀏覽器中,開發人員不用去操心每一幀動畫渲染的時間間隔問題,而針對低版本瀏覽器,則需要使用setTimeout來模擬requestAnimationFrame,且針對不同瀏覽器對requestAnimationFrame的實現,這個api的名字也略有差異,針對低版本瀏覽器的模擬requestAnimationFrame的寫法如下(來自張鑫旭大神的部落格):
(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);
};
}
}());
複製程式碼
具體含義不做解釋,如果遇到低版本瀏覽器的動畫需求,你只需要把這段程式碼丟進去定義一個低配版requestAnimationFrame方法即可(珍愛生命,遠離老瀏覽器啊)。
最後,再貼個部門工作中關於幀動畫的示例 帥氣的動畫效果
參考:
- JavaScript高階程式設計
- CSS3動畫那麼強,requestAnimationFrame還有毛線用?
如有錯誤,歡迎指正!