如何停止requestAnimationFrame方法啟動的動畫


HTML5/CSS3時代,我們要在web里做動畫選擇其實已經很多了:
(1) 你可以用CSS3的animattion+keyframes;
(2) 你也可以用css3的transition;

(3) 你還可以用通過在canvas上作圖來實現動畫,也可以借助jQuery動畫相關的API方便地實現;

(4) 當然最原始的你還可以使用window.setTimout()或者window.setInterval()通過不斷更新元素的狀態位置等來實現動畫,前提是畫面的更新頻率要達到每秒60次才能讓肉眼看到流暢的動畫效果。

現在又多了一種實現動畫的方案,那就是還在草案當中的  window.requestAnimationFrame()方法。

初識requestAnimationFrame

來看MDN上對其給出的詮釋:
   The window.requestAnimationFrame() method tells the browser that you wish to perform an animation and requests that the browser call a specified function to update an animation before the next repaint. The method takes as an argument a callback to be invoked before the repaint.

   window.requestAnimationFrame()將告知瀏覽器你馬上要開始動畫效果了,后者需要在下次動畫前調用相應方法來更新畫面。這個方法就是傳遞給window.requestAnimationFrame()的回調函數。

   也可這個方法原理其實也就跟setTimeout/setInterval差不多,通過遞歸調用同一方法來不斷更新畫面以達到動起來的效果,但它優於setTimeout/setInterval的地方在於它是由瀏覽器專門為動畫提供的API,在運行時瀏覽器會自動優化方法的調用,並且如果頁面不是激活狀態下的話,動畫會自動暫停,有效節省了CPU開銷。

基本語法
(1)可以直接調用,也可以通過window來調用,接收一個函數作為回調;
(2) 返回一個ID值,通過把這個ID值傳給window.cancelAnimationFrame()可以取消該次動畫。
    requestAnimationFrame(callback)//callback為回調函數

一個簡單的例子
  模擬一個進度條動畫,初始div寬度為1px,在step函數中將進度加1然后再更新到div寬度上,在進度達到100之前,一直重復這一過程。

為了演示方便加了一個運行按鈕(點擊觀看效果

<html>
 <head>
  <meta charset="gbk">
  <title>動畫</title>
 </head>
 <body>
  <div id="test" style="width:1px;height:17px;background:#0f0;">0%</div>
  <input type="button" value="Run" id="run"/>
  <input type="button" value="停止" id="stop"/>
 </body>
<script>

window.requestAnimationFrame =
   window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame 
                                || window.msRequestAnimationFrame;
  var start = null;
  var ele = document.getElementById("test");
  var progress = 0;
  var stop=null;
 function step(timestamp) {
    if(progress>=100) progress=0;
    progress += 1;
    ele.style.width = progress + "%";
    ele.innerHTML=progress + "%";
    if (progress < 100) {
        stop=requestAnimationFrame(step);
    }
}
document.getElementById("run").addEventListener("click", function() {
    ele.style.width = "1px";
   step();
}, false);
document.getElementById("stop").addEventListener("click", function() {
   window.cancelAnimationFrame(stop);//可以取消該次動畫。
}, false);
</script>
</html>

瀏覽器支持情況

既然還是草案狀態下引入的一個功能,在使用全我們就需要關心一下各瀏覽器對它的支持情況了。就目前來說,主流現代瀏覽器都對它提供了支持,請看下圖:

Polyfill

   Polyfill就是墊片,按發明這個詞的人的原話來說,它就是一段這樣的代碼,讓瀏覽器原生地支持我們期望使用的一些API。
就比如這里的requestAnimationFrame,在看到了上面的瀏覽器支持情況后,你就知道了比上面列出的瀏覽器版本老的就不支持該方法,但為了讓代碼能夠有更好的瀏覽器兼容性在老機器上也能運行不報錯,我們可以寫一些代碼讓瀏覽器在不支持requestAnimationFrame的情況下使用window.setTimeout(),這是一種回退(fallback)到過去的方法。

這樣一來,就可以通俗一點的理解polyfill了,它就是備胎。

   下面是由Paul Irish及其他貢獻者放在GitHub Gist上的代碼片段,用於在瀏覽器不支持requestAnimationFrame情況下的回退,回退到使用setTmeout的情況。當然,如果你確定代碼是工作在現代瀏覽器中,下面的代碼是不必的。

// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
// requestAnimationFrame polyfill by Erik M ller. fixes from Paul Irish and Tino Zijdel
// MIT license
(function() {
    var lastTime = 0;
    var vendors = ['ms', 'moz', 'webkit', 'o'];
    for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
        window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
        window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] +
          'CancelRequestAnimationFrame'];
    }
    if (!window.requestAnimationFrame) window.requestAnimationFrame = function(callback, element) {
        var currTime = new Date().getTime();
        var timeToCall = Math.max(0, 16 - (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方法時將其指向setTimeout方法。

   提到備胎代碼呢,這里多說一句,在CSS代碼中,我們也經常使用這種回退的技巧,即對同一條CSS規則,編寫多條以不同瀏覽器前綴開頭代碼,或者編寫一條備用樣式。

下面是一個CSS中的備胎代碼的例子:

div {
   background: rgb(0, 0, 0); /* fallback */
   background: rgba(0, 0, 0, 0.5);
 }

代碼中設置div背景為黑色帶50%的透明度,但IE9-的瀏覽器是不支持rbga格式的顏色的,所以瀏覽器會回退到上一條CSS規則應用rgb顏色。

Reference:
1. article about rAF from css tricks: http://css-tricks.com/using-requestanimationframe/
2. article about rAF from Paul Irish:http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
3. what is polyfill http://remysharp.com/2010/10/08/what-is-a-polyfill/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM