1. 程式人生 > 其它 >settimeout 引數_用setTimeout對比理解requestAnimationFrame

settimeout 引數_用setTimeout對比理解requestAnimationFrame

技術標籤:settimeout 引數

用setTimeout對比理解requestAnimationFrame

在H5標準出現之前,實現網頁動畫的方式無非就是 setTimeoutsetInterval ,但這種方式弊端很多,最常見的就是佔用資源高造成卡頓現象。H5和CSS3出現之後,便出現了很多動畫的實現方式,如CSS3中的 transitionanimation ,H5中的 canvas,還有一個比較常見但卻又是比較難理解的 requestAnimationFrame ,它的使用方法跟 setTimeoutsetInterval 比較接近,所以接下來我們就會通過對比 setTimeout

setInterval 來認識 requestAnimationFrame

setTimeout

先上一段程式碼:

HTML:

<style>
    .block{
        width: 100px;
        height: 100px;
        background-color: cyan;
    }
</style>
<body>
    <div class="block"></div>
    <script>
        var element = document.querySelector('.block');
        element.style.position = 'absolute';
    </script>
    <script src="./index.js"></script>
</body>

JavaScript:

let left = 1;
function step(){
    element.style.left = `${left++}px`;
    let timer = setTimeout(() => {
        step();
    }, 1000/60);
    if( timer && left > 100){
        clearTimeout(timer)
    }
}
step();

效果:

d2155148a016ecc50bc47d4e08b4e84a.png

setTimeout 的方式實現動畫是不難理解的,首先JS主執行緒中執行一次 step() 函式,step()

函式每次執行都會讓block元素往左移動一畫素,接著在主執行緒執行佇列中如果沒有其他任務,則在 1000/60ms 後再次執行 step() 函式,直到達到100畫素。之所以用 1000/60 是因為瀏覽器的重新整理率是每秒鐘60Hz,也就是1000毫秒內重新整理60次,所以需要(1000/60ms)16.67ms執行一次動畫才能達到比較流暢的效果。看到這裡,似乎沒有理由使用除了 setTimeout 之外的東西實現動畫了,因為它表現的的確很流暢。但是,這是在理想的情況下,如果在 EventLoop 執行佇列執行 setTimeout 回撥函式之前有其他任務等待執行(現實的Web應用往往存在大量的任務在執行佇列等待執行),那麼 setTimeout 回撥就會延遲執行,直到執行佇列輪到 setTimeout 回撥,所以誰也不能保證 setTimeout 回撥一定是在16.67ms後執行的。這樣就會導致動畫不流暢,甚至卡頓的現象發生,所以有必要引入一個新的專門用於動畫的API,他就是 requestAnimationFrame

requestAnimationFrame

let left = 1;
function step() {
  element.style.left = `${left}px`;
  left *= 1.02;
  if (left < 200) {
    window.requestAnimationFrame(step);
  }
}

window.requestAnimationFrame(step);

效果:

5579f72718296a0ca94d4c1e1fe85295.png
window.requestAnimationFrame() 告訴瀏覽器——你希望執行一個動畫,並且要求瀏覽器在下次重繪之前呼叫指定的回撥函式更新動畫。該方法需要傳入一個回撥函式作為引數,該回調函式會在瀏覽器下一次重繪之前執行
引自MDN —— window.requestAnimationFrame

requestAnimationFrame 的使用方法跟 setTimeout 區別不大,但是主要的區別是 requestAnimationFrame 的執行時機是瀏覽器去決定的,比如當前裝置重新整理率是60Hz,那它的執行就會是16.67ms。也可以說它的回撥函式的執行是與螢幕的重新整理有關,每刷完一次,就要執行一次,不會卡頓和掉幀。由於它定義的回撥是在下次重繪之前,所以必須在回撥裡再呼叫一次,才能實現連續的動畫。而且 requestAnimationFrame 在頁面隱藏掉或最小化的時候,是不會執行的,這樣就節省了一些不必要的系統資源。所以使用 requestAnimationFramesetTimeout 好處要多。