JS:指定FPS幀頻,requestAnimationFrame播放動畫
阿新 • • 發佈:2019-02-18
/* 抄自: http://www.cnblogs.com/kenkofox/p/3849067.html 建議大家看原版 這樣排版好*/
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();