隨着前端的發展,css已經可以實現很是多的動畫特效,可是仍然存在css沒法完成的動畫任務(好比頁面滾動),一般的解決方案都是使用js中的setInterval來設置定時器來實現動畫特效,好比下面的一個基本的動畫循環。javascript
(function() { function updateAnimations() { doAnimation1(); doAnimation2(); } setInterval(updateAnimations, 100); })();
該代碼實現的功能是每隔100毫秒執行函數操做來達到動畫效果,然而,使用計時器真的可靠嗎?
答案固然是 nocss
因爲JavaScript是單線程的,因此定時器的實現是在當前任務隊列完成後再執行定時器的回調的,假如當前隊列任務執行時間大於定時器設置的延遲時間,那麼定時器就不是那麼可靠了,以下所示:html
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毫秒後執行,實際執行延遲時間遠大於這個數值,這就會致使動畫效果並不會達到想要的效果。java
動畫是由瀏覽器按照必定的頻率一幀一幀的繪製的,由css實現的動畫的優點就是瀏覽器知道動畫的開始及每一幀的循環間隔,可以在恰當的時間刷新UI,給用戶一種流暢的體驗,而setInterval或setTimeout實現的JavaScript動畫就沒有這麼可靠了,由於瀏覽器壓根就沒法保證每一幀渲染的時間間隔,通常狀況下,每秒平均刷新次數可以達到60幀,就可以給人流暢的體驗,即每過 1000/60 毫秒渲染新一幀便可,但從上面的例子知,這一點單靠定時器是沒法保證的。
爲此,requestAnimationFrame應運而生,其做用就是讓瀏覽器流暢的執行動畫效果。能夠將其理解爲專門用來實現動畫效果的api,經過這個api,能夠告訴瀏覽器某個JavaScript代碼要執行動畫,瀏覽器收到通知後,則會運行這些代碼的時候進行優化,實現流暢的效果,而再也不須要開發人員煩心刷新頻率的問題了。css3
使用方法以下:git
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錄製的有點卡。。。實際效果請參考示例):
github
能夠看到,requestAnimationFrame接受一個動畫執行函數做爲參數,這個函數的做用是僅執行一幀動畫的渲染,並根據條件判斷是否結束,若是動畫沒有結束,則繼續調用requestAnimationFrame並將自身做爲參數傳入。從示例來看,獲得了效果平滑流暢的動畫,這樣就巧妙地避開了每一幀動畫渲染的時間間隔問題。web
requestAnimationFrame的兼容性參考caniuseapi
在高級瀏覽器中,開發人員不用去操心每一幀動畫渲染的時間間隔問題,而針對低版本瀏覽器,則須要使用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方法便可(珍愛生命,遠離老瀏覽器啊)。
最後,再貼個部門工做中關於幀動畫的示例
帥氣的動畫效果
參考:
若有錯誤,歡迎指正!