JS:指定FPS幀頻,requestAnimationFrame播放動畫


Flash制作動畫,最基礎的概念就是幀,但在Flash中,幀頻的控制比較簡單,只需要編譯前指定一下目標幀頻就可以了。

實際運行時,不需要我們關心定時器的問題,flash player會定時觸發EnterFrame消息,推動Movieclip播放。

在js這一側,需要我們設定一個定時器,並推動相應的繪制邏輯執行。

最簡單:

var FPS = 60;
 
setInterval(draw, 1000/FPS);

 

這個簡單做法,如果draw帶有大量邏輯計算,導致計算時間超過幀等待時間時,將會出現丟幀。除外,如果FPS太高,超過了當時瀏覽器的重繪頻率,將會造成計算浪費,例如瀏覽器實際才重繪2幀,但卻計算了3幀,那么有1幀的計算就浪費了。

成熟做法:

引入requestAnimationFrame,這個方法是用來在頁面重繪之前,通知瀏覽器調用一個指定的函數,以滿足開發者操作動畫的需求。

這個函數類似setTimeout,只調用一次。

function draw() {
requestAnimationFrame(draw);
// ... Code for Drawing the Frame ...
}

 

遞歸調用,就可以實現定時器。

但是,這樣完全跟瀏覽器幀頻同步了,無法自定義動畫的幀頻,是無法滿足需求的。

接下來需要考慮如何控制幀頻。

簡單做法:

 

var fps = 30;
function tick() {
  setTimeout(function() {
    requestAnimationFrame(tick);
    draw(); // ... Code for Drawing the Frame ...
  }, 1000 / fps);
}
tick();

 

這種做法,比較直觀的可以發現,每一次setTimeout執行的時候,都還要再等到下一個requestAnimationFrame事件到達,累積下去會造成動畫變慢。

自行控制時間跨度:

   

var fps = 30;
var now;
var then = Date.now();
var interval = 1000/fps;
var delta;
 
function tick() {
  requestAnimationFrame(tick);
  now = Date.now();
  delta = now - then;
  if (delta > interval) {
    // 這里不能簡單then=now,否則還會出現上邊簡單做法的細微時間差問題。例如fps=10,每幀100ms,而現在每16ms(60fps)執行一次draw。16*7=112>100,需要7次才實際繪制一次。這個情況下,實際10幀需要112*10=1120ms>1000ms才繪制完成。
    then = now - (delta % interval);
    draw(); // ... Code for Drawing the Frame ...
  }
}
tick();

 

針對低版本瀏覽器再優化:

如果瀏覽器沒有requestAnimationFrame函數,實際底層還只能用setTimeout模擬,上邊做的都是無用功。那么可以再改進一下。

var fps = 30;
var now;
var then = Date.now();
var interval = 1000/fps;
var delta;
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
 
function tick() {
  if(window.requestAnimationFrame)
{
   requestAnimationFrame(tick);
   now = Date.now();
   delta = now - then;
   if (delta > interval) {
// 這里不能簡單then=now,否則還會出現上邊簡單做法的細微時間差問題。例如fps=10,每幀100ms,而現在每16ms(60fps)執行一次draw。16*7=112>100,需要7次才實際繪制一次。這個情況下,實際10幀需要112*10=1120ms>1000ms才繪制完成。
     then = now - (delta % interval);
     draw(); // ... Code for Drawing the Frame ...
   }
}
else
{
setTimeout(tick, interval);
    draw();
}
}
tick();

 

 

最后,還可以加上暫停。

var fps = 30;
var pause = false;
var now;
var then = Date.now();
var interval = 1000/fps;
var delta;
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
 
function tick() {
if(pause)
return;
  if(window.requestAnimationFrame)
{
    ...
}
else
{
...
}
}
tick();

 

 


免責聲明!

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



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