淺談 requestAnimationFrame

背景

在Web應用中,實現動畫效果的方法比較多,Javascript 中能夠經過定時器 setTimeout或者setInterval 來實現,css3 可使用 transition 和 animation 來實現,html5 中的 canvas 也能夠實現。除此以外,html5 還提供一個專門用於請求動畫的API,那就是 requestAnimationFrame,顧名思義就是請求動畫幀。javascript

可是傳統的經過setTimeout或者setInterval實現的動畫,存在兩個問題,第一個就是動畫的循時間環間隔很差肯定,設置長了動畫顯得不夠平滑流暢, 設置短了瀏覽器的重繪頻率會達到瓶頸,推薦的最佳循環間隔是17ms(大多數電腦的顯示器刷新頻率是60Hz,1000ms / 60),第二個問題是定時器第二個時間參數只是指定了多久後將動畫任務添加到瀏覽器的UI線程隊列中,若是UI線程處於忙碌狀態,那麼動畫不會馬上執行,爲了解決這個問題,H5中加入了requestAnimationFrame。css

實現一個動畫

完成一個簡單的移動動畫,咱們可使用setInterval和requestAnimationFrame兩種方式實現。html

setInterval

<html>
    <head>
        <title></title>
        <style type="text/css">
            #box {
                margin: 200px;
                width: 200px;
                height: 200px;
                background: green;
            }
        </style>
    </head>

    <body>
        <div id="box"></div>
    </body>
    <script type="text/javascript">
        var element = document.getElementById('box')
        var left = 0;
        var animateCallback = function() {
            element.style.marginLeft = (++left)+ 'px';
            if (left === 500) {
                clearInterval(interval);
            }
        }
        var interval = setInterval(animateCallback, (1000 / 60));
    </script>
複製代碼

使用setInterval咱們成功的實現了一個平滑的動畫效果,可是咱們會發現setInterval的執行時間並不肯定,在javascript中,setInterval任務被放進了異步隊列中,只有當主線程上的任務執行完之後,纔會去檢查該隊列裏的任務是否須要開始執行,因此setInterval的實際執行時機通常要比其設定的時間晚一些。使用setInterval實現動畫容易失幀。html5

什麼是丟幀

例如咱們使用setInterval進行顏色的切換java

var color = ['green', 'red', 'blue', 'yellow'];
var element = document.getElementById('box');
var index = 0;
var animateCallback = function() {
    index++;
    element.style.backgroundColor = color[index];
    if (index === 3){
        clearInterval(interval);
    }
}
var interval = setInterval(interval, 1000 / 100);
複製代碼

上面的動畫切換咱們設置了間隔10切換一次,可是此時的屏幕刷新頻率爲16.7,css3

  • 第0ms時,屏幕未刷新,等待中,setInterval也未執行,等待中;
  • 第10ms時,屏幕未刷新,等待中,setInterval執行顏色切換爲green
  • 第16.7ms時: 屏幕刷新,屏幕的box顏色改變爲green,setInterval未執行。繼續等待
  • 第20ms時:屏幕未刷新,等待中,setInterval執行顏色切換爲red,
  • 第30ms時,屏幕未刷新,等待中,setInterval執行顏色切換爲blue,
  • 第34.7ms時,屏幕刷新,屏幕的box顏色改變爲blue,setTimeout未執行,繼續等待中。
  • ...

從上面的執行過程當中,咱們能夠看出,在執行到20ms和30ms直接,setInterval切換了兩次顏色,可是屏幕並無執行一次刷新,就會出現,在34.7ms時,屏幕上直接展現的就是blue顏色,而跳過了red顏色。這就是丟幀現象,這種現象也會引發頁面卡頓。canvas

requstAnimationFrame實現

<html>
    <head>
        <title></title>
        <style type="text/css">
            #box {
                margin: 200px;
                width: 200px;
                height: 200px;
                background: green;
            }
        </style>
    </head>

    <body>
        <div id="box"></div>
    </body>
    <script type="text/javascript">
        var start = null;
        var element = document.getElementById('box');
        var left = 0;
        var raf_id = null;
        function animateCallback() {
            element.style.marginLeft = (++left) + 'px';
            if (left === 500) {
                cancelAnimationFrame(raf_id);
            } else {
                raf_id = requestAnimationFrame( animateCallback );
            }
        }
        raf_id =  window.requestAnimationFrame(animateCallback);
    </script>
複製代碼

requestAnimationFrame返回請求的id(整數),咱們可使用這個id來取消請求(cancelAnimationFram(id)),從而中止動畫的執行。 和setInterval相比,requestAnimationFrame最大的優點是由系統來決定回調函數的執行時機,requestAnimationFrame的步伐跟着系統的刷新步伐走,它能保證回調函數在屏幕每一次的刷新間隔中只被執行一次,這樣就不會引發丟幀。瀏覽器

requestAnimationFrame優點

CPU節能

使用setTimeout實現的動畫,當頁面被隱藏或最小化時,setTimeout仍然在後臺執行動畫任務,此時頁面處於不可見或者不可用的狀態,刷新動畫是沒有意義的,並且還浪費CPU資源,而rAF則徹底不一樣,當頁面處於未激活的狀態下,該頁面的屏幕繪製任務也會被系統暫停,所以跟着系統步伐走的rAF也會中止渲染,當頁面被激活時,動畫就從上次停留的地方繼續執行,有效節省了CPU開銷。bash

函數節流

在高頻率事件(resize, scroll)中,爲了防止在一個刷新間隔內發生屢次函數執行,使用rAF可保證每次繪製間隔內,函數只被執行一次,這樣既能保證流暢性,也能更好的節省函數執行的開銷,一個繪製間隔內函數執行屢次是沒有意義的,由於顯示器每16.7ms繪製一次,屢次繪製並不會在屏幕上體現出來。異步

相關文章
相關標籤/搜索