風和日麗,饒有興致,翻開以前寫的一個簡單的動畫插件,發現是用定時器寫的,可是做爲有追求的前端,一個問題怎麼能有一種解決方案呢?故而,遍尋資料,終於看見曙光,讓我查到了requestAnimationFrame 這個宿主對象的方法,也能能優雅的實現js動畫!css
在咱們前端的傳統中,在古老的ie稱霸的年代,咱們想要實現動畫,必需要藉助setTimeout或setInterval這兩個函數,下面咱們來思考一個問題:html
咱們讓一個數字從0開始逐漸變大,到達100時在逐漸變小,如此往復 前端
那麼,傳統的定時器的寫法應該怎麼寫呢?廢話少說上代碼html5
//css部分 <style> #a { font-size: 30px; } </style> 複製代碼
//html部分 <div id="a"></div> 複製代碼
//js部分 var e = document.getElementById("a"); var flag = true; var left = 0; function render() { if (flag == true) { if (left >= 100) { flag = false; } e.innerHTML = left++; } else { if (left <= 0) { flag = true; } e.innerHTML = left--; } } setInterval(function () { render(); }, 1000/60); 複製代碼
以上寫法即可以實現循環往復的變大變小的操做!瀏覽器
這種方法,可行嗎?固然可行,完美嗎?也還算完美,當忽然發現新大陸之後,定時器便完全被終結了,就好比,你用了蘋果的Retina屏幕之後,發現再也回不去了是一個道理,你說1080p的屏幕完美嗎?挺完美的,然而當你拿到Retina之後,直呼真香!bash
在瞭解requestAnimationFrame以前,咱們先來了解幾個概念,闡述一下爲啥requestAnimationFrame真香markdown
之因此咱們能看到動畫,一些動畫效果,徹底時由咱們的顯示器在短期內不斷播放一張張圖片,當播放速率過快時,便造成了動畫效果,而咱們的顯示器在播放圖片時,通常有一個播放的頻率標準,咱們叫作屏幕刷新率,即圖像在屏幕上更新的速度,也即屏幕上的圖像每秒鐘出現的次數,它的單位是赫茲(Hz)。通常狀況下,當刷新率達到60hz基本咱們的肉眼就感受不到他是靜態的了,變成了一個連貫的動畫!異步
那你可知這是爲何呢?函數
爲何你感受不到這個變化? 那是由於人的眼睛有視覺停留效應,即前一副畫面留在大腦的印象還沒消失,緊接着後一副畫面就跟上來了,這中間只間隔了16.7ms(1000/60≈16.7), 因此會讓你誤覺得屏幕上的圖像是靜止不動的。而屏幕給你的這種感受是對的,試想一下,若是刷新頻率變成1次/秒,屏幕上的圖像就會出現嚴重的閃爍,這樣就很容易引發眼睛疲勞、痠痛和頭暈目眩等症狀。(跟主題沒啥關係,強行科普一波)oop
因爲高刷新率的存在,加上人眼睛的視覺停留效應,理解動畫的原理就變得很是簡單了。 畫本質就是要讓人眼看到圖像被刷新而引發變化的視覺效果,這個變化要以連貫的、平滑的方式進行過渡。,若是一來,在咱們的瀏覽器,中就能看到連貫的動畫效果
上面的講述你應該已經大概知道定時器能實現動畫效果了,其實他就是經過不斷改變這個元素的位置或者值,來達到快速播放靜圖片的效果,從而造成一個完整的動畫
然而因爲定時器的在js中的執行方式,致使它有一些小小的瑕疵,雖然能夠忍受,可是有更好的東西出來,爲啥不淘汰掉他呢?
咱們知道定時器的執行時間並非肯定的。這是因爲js是個單線程的語言,他必須使用異步,來解決一些須要延時執行這個問題,那麼爲何說定時器的執行時間不是肯定的呢?那就得來細數一下輪詢了
Event Loop的是計算機系統的一種運行機制。JavaScript語言就採用這種機制,來解決單線程運行帶來的一些問題。
在JavaScript中,任務被分爲兩種,一種宏任務(MacroTask)也叫Task,一種叫微任務(MicroTask)。
常見的宏任務有:script所有代碼、setTimeout、setInterval、setImmediate(瀏覽器暫時不支持,只有IE10支持,具體可見MDN)、I/O、UI Rendering。
常見的微任務有:Process.nextTick(Node獨有)、Promise、Object.observe(廢棄)、MutationObserver
下面來簡單學習一下Event Loop的執行過程
最後,微任務執行完畢,清空執行棧,拿到隊列中的下一次Event Loop的內容,在開始執行,走到這裏,你會發現,在定時器執行的時候,前面還有會一堆同步代碼也須要時間,若是前面有個循環個三五百次的話,會很是浪費時間,這就暴露出了定時器的一個缺點:丟幀現象,就是每次間隔實際上是不肯定的,致使跟瀏覽器的刷刷新率匹配不上,有可能出現的丟幀現象!(後通過大佬更正,定時器丟幀的緣由僅僅是沒有被瀏覽器的策略干涉,並非會被同步任務阻塞)
//這段代碼能夠證明 requestAnimationFrame(() => console.log("Hello World")); while (true); 複製代碼
看完流程之後,請仔細參悟上圖,會有收穫的!
requestAnimationFrame是html5 提供的一個專門用於請求動畫的API,顧名思義就是請求動畫幀,他被封裝在宿主對象中, window.requestAnimationFrame() 告訴瀏覽器——你但願執行一個動畫,而且要求瀏覽器在下次重繪以前調用指定的回調函數更新動畫。該方法須要傳入一個回調函數做爲參數,該回調函數會在瀏覽器下一次重繪以前執行
//html <div id="a"></div> //js <script> var e = document.getElementById("a"); var flag = true; var left = 0; function render() { if (flag == true) { if (left >= 100) { flag = false; } e.innerHTML = left++; } else { if (left <= 0) { flag = true; } e.innerHTML = left--; } } // setInterval(function () { // render(); // }, 1000); (function animloop() { render(); var a=window.requestAnimationFrame(animloop); })(); 複製代碼
到這裏就能夠結束了,可是忽然有冒出來個疑問,他怎麼中止呢?
執行函數放回一個id是回調列表中惟一的標識。是個非零值,沒別的意義。你能夠傳這個值給 window.cancelAnimationFrame() 以取消回調函數。
window.cancelAnimationFrame(a)//注意這個要寫在函數體內部,
複製代碼
到這裏,基本就結束了requestAnimationFrame的探索,因爲我也是邊學邊寫,不對之處,請大佬指正!