Sepertinya requestAnimationFrame
ini cara de facto untuk menganimasikan sesuatu sekarang. Ini bekerja cukup baik bagi saya untuk sebagian besar, tetapi saat ini saya mencoba melakukan beberapa animasi kanvas dan saya bertanya-tanya: Apakah ada cara untuk memastikannya berjalan pada fps tertentu? Saya memahami bahwa tujuan rAF adalah untuk animasi yang mulus secara konsisten, dan saya mungkin berisiko membuat animasi saya berombak, tetapi saat ini tampaknya berjalan pada kecepatan yang sangat berbeda secara sewenang-wenang, dan saya bertanya-tanya apakah ada cara untuk melawannya. itu entah bagaimana.
Saya akan menggunakan setInterval
tetapi saya ingin pengoptimalan yang ditawarkan rAF (terutama secara otomatis berhenti ketika tab dalam fokus).
Jika seseorang ingin melihat kode saya, itu cukup banyak:
animateFlash: function() {
ctx_fg.clearRect(0,0,canvasWidth,canvasHeight);
ctx_fg.fillStyle = 'rgba(177,39,116,1)';
ctx_fg.strokeStyle = 'none';
ctx_fg.beginPath();
for(var i in nodes) {
nodes[i].drawFlash();
}
ctx_fg.fill();
ctx_fg.closePath();
var instance = this;
var rafID = requestAnimationFrame(function(){
instance.animateFlash();
})
var unfinishedNodes = nodes.filter(function(elem){
return elem.timer < timerMax;
});
if(unfinishedNodes.length === 0) {
console.log("done");
cancelAnimationFrame(rafID);
instance.animate();
}
}
Dimana Node.drawFlash () hanyalah beberapa kode yang menentukan radius berdasarkan variabel counter dan kemudian menggambar sebuah lingkaran.
sumber
requestAnimationFrame
adalah (seperti namanya) untuk meminta bingkai animasi hanya jika diperlukan. Katakanlah Anda menunjukkan kanvas hitam statis, Anda harus mendapatkan 0 fps karena tidak diperlukan bingkai baru. Tetapi jika Anda menampilkan animasi yang membutuhkan 60fps, Anda juga harus mendapatkannya.rAF
hanya memungkinkan untuk "melewati" frame yang tidak berguna dan kemudian menyimpan CPU.Jawaban:
Cara membatasi requestAnimationFrame ke frekuensi gambar tertentu
Pelambatan demo pada 5 FPS: http://jsfiddle.net/m1erickson/CtsY3/
Metode ini bekerja dengan menguji waktu yang telah berlalu sejak menjalankan perulangan bingkai terakhir.
Kode gambar Anda dijalankan hanya ketika interval FPS yang Anda tentukan telah berlalu.
Bagian pertama dari kode menetapkan beberapa variabel yang digunakan untuk menghitung waktu yang telah berlalu.
var stop = false; var frameCount = 0; var $results = $("#results"); var fps, fpsInterval, startTime, now, then, elapsed; // initialize the timer variables and start the animation function startAnimating(fps) { fpsInterval = 1000 / fps; then = Date.now(); startTime = then; animate(); }
Dan kode ini adalah loop requestAnimationFrame aktual yang diambil pada FPS yang Anda tentukan.
// the animation loop calculates time elapsed since the last loop // and only draws if your specified fps interval is achieved function animate() { // request another frame requestAnimationFrame(animate); // calc elapsed time since last loop now = Date.now(); elapsed = now - then; // if enough time has elapsed, draw the next frame if (elapsed > fpsInterval) { // Get ready for next frame by setting then=now, but also adjust for your // specified fpsInterval not being a multiple of RAF's interval (16.7ms) then = now - (elapsed % fpsInterval); // Put your drawing code here } }
sumber
Perbarui 2016/6
Masalah membatasi kecepatan bingkai adalah layar memiliki kecepatan pembaruan konstan, biasanya 60 FPS.
Jika kita menginginkan 24 FPS, kita tidak akan pernah mendapatkan 24 fps yang sebenarnya di layar, kita dapat mengatur waktunya seperti itu tetapi tidak menampilkannya karena monitor hanya dapat menampilkan bingkai yang disinkronkan pada 15 fps, 30 fps atau 60 fps (beberapa monitor juga 120 fps ).
Namun, untuk tujuan pengaturan waktu, kami dapat menghitung dan memperbarui jika memungkinkan.
Anda bisa membangun semua logika untuk mengontrol frekuensi gambar dengan merangkum penghitungan dan callback ke dalam objek:
function FpsCtrl(fps, callback) { var delay = 1000 / fps, // calc. time per frame time = null, // start time frame = -1, // frame count tref; // rAF time reference function loop(timestamp) { if (time === null) time = timestamp; // init start time var seg = Math.floor((timestamp - time) / delay); // calc frame no. if (seg > frame) { // moved to next frame? frame = seg; // update callback({ // callback function time: timestamp, frame: frame }) } tref = requestAnimationFrame(loop) } }
Kemudian tambahkan beberapa pengontrol dan kode konfigurasi:
// play status this.isPlaying = false; // set frame-rate this.frameRate = function(newfps) { if (!arguments.length) return fps; fps = newfps; delay = 1000 / fps; frame = -1; time = null; }; // enable starting/pausing of the object this.start = function() { if (!this.isPlaying) { this.isPlaying = true; tref = requestAnimationFrame(loop); } }; this.pause = function() { if (this.isPlaying) { cancelAnimationFrame(tref); this.isPlaying = false; time = null; frame = -1; } };
Pemakaian
Ini menjadi sangat sederhana - sekarang, yang harus kita lakukan adalah membuat instance dengan mengatur fungsi panggilan balik dan frekuensi gambar yang diinginkan seperti ini:
var fc = new FpsCtrl(24, function(e) { // render each frame here });
Kemudian mulailah (yang bisa menjadi perilaku default jika diinginkan):
Itu saja, semua logika ditangani secara internal.
Demo
var ctx = c.getContext("2d"), pTime = 0, mTime = 0, x = 0; ctx.font = "20px sans-serif"; // update canvas with some information and animation var fps = new FpsCtrl(12, function(e) { ctx.clearRect(0, 0, c.width, c.height); ctx.fillText("FPS: " + fps.frameRate() + " Frame: " + e.frame + " Time: " + (e.time - pTime).toFixed(1), 4, 30); pTime = e.time; var x = (pTime - mTime) * 0.1; if (x > c.width) mTime = pTime; ctx.fillRect(x, 50, 10, 10) }) // start the loop fps.start(); // UI bState.onclick = function() { fps.isPlaying ? fps.pause() : fps.start(); }; sFPS.onchange = function() { fps.frameRate(+this.value) }; function FpsCtrl(fps, callback) { var delay = 1000 / fps, time = null, frame = -1, tref; function loop(timestamp) { if (time === null) time = timestamp; var seg = Math.floor((timestamp - time) / delay); if (seg > frame) { frame = seg; callback({ time: timestamp, frame: frame }) } tref = requestAnimationFrame(loop) } this.isPlaying = false; this.frameRate = function(newfps) { if (!arguments.length) return fps; fps = newfps; delay = 1000 / fps; frame = -1; time = null; }; this.start = function() { if (!this.isPlaying) { this.isPlaying = true; tref = requestAnimationFrame(loop); } }; this.pause = function() { if (this.isPlaying) { cancelAnimationFrame(tref); this.isPlaying = false; time = null; frame = -1; } }; }
body {font:16px sans-serif}
<label>Framerate: <select id=sFPS> <option>12</option> <option>15</option> <option>24</option> <option>25</option> <option>29.97</option> <option>30</option> <option>60</option> </select></label><br> <canvas id=c height=60></canvas><br> <button id=bState>Start/Stop</button>
Jawaban lama
Tujuan utamanya
requestAnimationFrame
adalah untuk menyinkronkan pembaruan ke kecepatan refresh monitor. Ini akan meminta Anda untuk menganimasikan pada FPS monitor atau faktornya (mis. 60, 30, 15 FPS untuk kecepatan refresh khas @ 60 Hz).Jika Anda menginginkan FPS yang lebih sewenang-wenang maka tidak ada gunanya menggunakan rAF karena laju bingkai tidak akan pernah cocok dengan frekuensi pembaruan monitor (hanya bingkai di sana-sini) yang tidak dapat memberi Anda animasi yang mulus (seperti dengan semua pengaturan waktu bingkai ) dan Anda juga dapat menggunakan
setTimeout
atausetInterval
sebagai gantinya.Ini juga merupakan masalah umum dalam industri video profesional ketika Anda ingin memutar video pada FPS yang berbeda kemudian perangkat menampilkannya menyegarkan. Banyak teknik telah digunakan seperti pencampuran bingkai dan pengaturan waktu ulang yang kompleks membangun kembali bingkai perantara berdasarkan vektor gerak, tetapi dengan kanvas teknik ini tidak tersedia dan hasilnya akan selalu berupa video tersentak-sentak.
var FPS = 24; /// "silver screen" var isPlaying = true; function loop() { if (isPlaying) setTimeout(loop, 1000 / FPS); ... code for frame here }
Alasan mengapa kita menempatkan
setTimeout
pertama (dan mengapa beberapa tempatrAF
pertama ketika poly-fill digunakan) adalah karena ini akan lebih akurat karenasetTimeout
akan mengantri suatu acara segera ketika loop dimulai sehingga tidak peduli berapa banyak waktu yang tersisa kode akan digunakan (asalkan tidak melebihi interval batas waktu) panggilan berikutnya akan berada pada interval yang diwakilinya (untuk rAF murni ini tidak penting karena rAF akan mencoba untuk melompat ke frame berikutnya dalam hal apapun).Juga perlu dicatat bahwa menempatkannya terlebih dahulu juga akan berisiko panggilan bertumpuk seperti halnya
setInterval
.setInterval
mungkin sedikit lebih akurat untuk penggunaan ini.Dan Anda dapat menggunakan
setInterval
bukan di luar loop untuk melakukan hal yang sama.var FPS = 29.97; /// NTSC var rememberMe = setInterval(loop, 1000 / FPS); function loop() { ... code for frame here }
Dan untuk menghentikan loop:
clearInterval(rememberMe);
Untuk mengurangi frekuensi gambar saat tab menjadi buram, Anda dapat menambahkan faktor seperti ini:
var isFocus = 1; var FPS = 25; function loop() { setTimeout(loop, 1000 / (isFocus * FPS)); /// note the change here ... code for frame here } window.onblur = function() { isFocus = 0.5; /// reduce FPS to half } window.onfocus = function() { isFocus = 1; /// full FPS }
Dengan cara ini Anda dapat mengurangi FPS menjadi 1/4 dll.
sumber
requestAnimationFrame
adalah untuk menyinkronkan operasi DOM (baca / tulis) sehingga tidak menggunakannya akan mengganggu kinerja saat mengakses DOM, karena operasi tidak akan diantrekan untuk dilakukan bersama dan akan memaksa pengecatan ulang tata letak yang tidak perlu.Saya sarankan untuk mengakhiri panggilan Anda ke
requestAnimationFrame
dalamsetTimeout
:const fps = 25; function animate() { // perform some animation task here setTimeout(() => { requestAnimationFrame(animate); }, 1000 / fps); } animate();
Anda perlu memanggil
requestAnimationFrame
dari dalamsetTimeout
, bukan sebaliknya, karenarequestAnimationFrame
menjadwalkan fungsi Anda untuk berjalan tepat sebelum pengecatan ulang berikutnya, dan jika Anda menunda pembaruan lebih lanjut menggunakansetTimeout
Anda akan melewatkan jendela waktu itu. Namun, melakukan sebaliknya adalah hal yang wajar, karena Anda hanya menunggu beberapa saat sebelum membuat permintaan.sumber
Ini semua adalah ide bagus secara teori, sampai Anda mendalami. Masalahnya adalah Anda tidak dapat membatasi RAF tanpa melakukan de-sinkronisasi, mengalahkan tujuannya untuk yang sudah ada. Jadi Anda membiarkannya berjalan dengan kecepatan penuh, dan memperbarui data Anda dalam loop terpisah , atau bahkan utas terpisah!
Ya, saya mengatakannya. Anda dapat melakukan JavaScript multi-utas di browser!
Ada dua metode yang saya tahu bekerja sangat baik tanpa jank, menggunakan lebih sedikit jus dan menghasilkan lebih sedikit panas. Penentuan waktu skala manusia yang akurat dan efisiensi mesin adalah hasil bersihnya.
Maaf jika ini sedikit bertele-tele, tapi ini dia ...
Metode 1: Perbarui data melalui setInterval, dan grafik melalui RAF.
Gunakan setInterval terpisah untuk memperbarui nilai terjemahan dan rotasi, fisika, tabrakan, dll. Simpan nilai tersebut dalam sebuah objek untuk setiap elemen animasi. Tetapkan string transformasi ke variabel di objek setiap 'frame' setInterval. Simpan objek-objek ini dalam satu larik. Atur interval Anda ke fps yang Anda inginkan dalam ms: ms = (1000 / fps). Ini menjaga jam tetap stabil yang memungkinkan fps yang sama pada perangkat apa pun, terlepas dari kecepatan RAF. Jangan tetapkan transformasi ke elemen di sini!
Dalam loop requestAnimationFrame, lakukan iterasi melalui array Anda dengan loop for-sekolah lama-- jangan gunakan formulir yang lebih baru di sini, mereka lambat!
for(var i=0; i<sprite.length-1; i++){ rafUpdate(sprite[i]); }
Dalam fungsi rafUpdate Anda, dapatkan string transformasi dari objek js Anda dalam array, dan elemennya id. Anda harus sudah memiliki elemen 'sprite' yang dilampirkan ke variabel atau mudah diakses melalui cara lain sehingga Anda tidak kehilangan waktu untuk 'mendapatkannya' di RAF. Menyimpannya dalam objek yang dinamai menurut id html mereka bekerja dengan cukup baik. Atur bagian itu bahkan sebelum masuk ke SI atau RAF Anda.
Gunakan RAF untuk memperbarui transformasi Anda saja , gunakan hanya transformasi 3D (bahkan untuk 2d), dan setel css "will-change: transform;" pada elemen yang akan berubah. Ini membuat transformasi Anda tetap tersinkronisasi ke kecepatan refresh asli sebanyak mungkin, bekerja di GPU, dan memberi tahu browser tempat untuk paling berkonsentrasi.
Jadi Anda harus memiliki sesuatu seperti ini pseudocode ...
// refs to elements to be transformed, kept in an array var element = [ mario: document.getElementById('mario'), luigi: document.getElementById('luigi') //...etc. ] var sprite = [ // read/write this with SI. read-only from RAF mario: { id: mario ....physics data, id, and updated transform string (from SI) here }, luigi: { id: luigi .....same } //...and so forth ] // also kept in an array (for efficient iteration) //update one sprite js object //data manipulation, CPU tasks for each sprite object //(physics, collisions, and transform-string updates here.) //pass the object (by reference). var SIupdate = function(object){ // get pos/rot and update with movement object.pos.x += object.mov.pos.x; // example, motion along x axis // and so on for y and z movement // and xyz rotational motion, scripted scaling etc // build transform string ie object.transform = 'translate3d('+ object.pos.x+','+ object.pos.y+','+ object.pos.z+ ') '+ // assign rotations, order depends on purpose and set-up. 'rotationZ('+object.rot.z+') '+ 'rotationY('+object.rot.y+') '+ 'rotationX('+object.rot.x+') '+ 'scale3d('.... if desired ; //...etc. include } var fps = 30; //desired controlled frame-rate // CPU TASKS - SI psuedo-frame data manipulation setInterval(function(){ // update each objects data for(var i=0; i<sprite.length-1; i++){ SIupdate(sprite[i]); } },1000/fps); // note ms = 1000/fps // GPU TASKS - RAF callback, real frame graphics updates only var rAf = function(){ // update each objects graphics for(var i=0; i<sprite.length-1; i++){ rAF.update(sprite[i]) } window.requestAnimationFrame(rAF); // loop } // assign new transform to sprite's element, only if it's transform has changed. rAF.update = function(object){ if(object.old_transform !== object.transform){ element[object.id].style.transform = transform; object.old_transform = object.transform; } } window.requestAnimationFrame(rAF); // begin RAF
Ini membuat pembaruan Anda ke objek data dan mengubah string disinkronkan ke tingkat 'bingkai' yang diinginkan di SI, dan tugas transformasi aktual di RAF disinkronkan ke kecepatan penyegaran GPU. Jadi pembaruan grafik sebenarnya hanya di RAF, tetapi perubahan pada data, dan membangun string transformasi ada di SI, sehingga tidak ada jankies tetapi 'waktu' mengalir pada kecepatan bingkai yang diinginkan.
Mengalir:
[setup js sprite objects and html element object references] [setup RAF and SI single-object update functions] [start SI at percieved/ideal frame-rate] [iterate through js objects, update data transform string for each] [loop back to SI] [start RAF loop] [iterate through js objects, read object's transform string and assign it to it's html element] [loop back to RAF]
Metode 2. Letakkan SI di web-worker. Yang ini FAAAST dan mulus!
Sama seperti metode 1, tetapi letakkan SI di pekerja web. Ini akan berjalan pada utas yang benar-benar terpisah, meninggalkan halaman untuk hanya berurusan dengan RAF dan UI. Meneruskan array sprite bolak-balik sebagai 'objek yang dapat ditransfer'. Ini buko cepat. Tidak perlu waktu untuk mengkloning atau membuat serial, tetapi tidak seperti melewatkan referensi di mana referensi dari sisi lain dihancurkan, jadi Anda harus meminta kedua sisi lolos ke sisi lain, dan hanya memperbaruinya saat ada, sortir seperti menyampaikan pesan bolak-balik dengan pacar Anda di sekolah menengah.
Hanya satu yang dapat membaca dan menulis dalam satu waktu. Ini baik-baik saja selama mereka memeriksa apakah itu tidak ditentukan untuk menghindari kesalahan. RAF CEPAT dan akan segera menendangnya, lalu melewati sekumpulan bingkai GPU hanya untuk memeriksa apakah sudah dikirim kembali. SI di web-worker sebagian besar akan memiliki array sprite, dan akan memperbarui data posisi, pergerakan dan fisika, serta membuat string transformasi baru, lalu meneruskannya kembali ke RAF di halaman.
Ini adalah cara tercepat yang saya tahu untuk menganimasikan elemen melalui skrip. Kedua fungsi ini akan berjalan sebagai dua program terpisah, pada dua utas terpisah, memanfaatkan CPU multi-core dengan cara yang tidak dimiliki skrip js tunggal. Animasi javascript multi-utas.
Dan itu akan melakukannya dengan lancar tanpa jank, tetapi pada kecepatan bingkai yang ditentukan sebenarnya, dengan sedikit perbedaan.
Hasil:
Salah satu dari kedua metode ini akan memastikan skrip Anda akan berjalan dengan kecepatan yang sama di PC, ponsel, tablet, dll. (Dalam kemampuan perangkat dan browser, tentu saja).
sumber
visibilitychange
acaranya.Cara dengan mudah mencekik ke FPS tertentu:
// timestamps are ms passed since document creation. // lastTimestamp can be initialized to 0, if main loop is executed immediately var lastTimestamp = 0, maxFPS = 30, timestep = 1000 / maxFPS; // ms for each frame function main(timestamp) { window.requestAnimationFrame(main); // skip if timestep ms hasn't passed since last frame if (timestamp - lastTimestamp < timestep) return; lastTimestamp = timestamp; // draw frame here } window.requestAnimationFrame(main);
Sumber: Penjelasan Mendetail tentang JavaScript Game Loops and Timing oleh Isaac Sukin
sumber
Melewati requestAnimationFrame menyebabkan animasi tidak mulus (diinginkan) pada fps khusus.
// Input/output DOM elements var $results = $("#results"); var $fps = $("#fps"); var $period = $("#period"); // Array of FPS samples for graphing // Animation state/parameters var fpsInterval, lastDrawTime, frameCount_timed, frameCount, lastSampleTime, currentFps=0, currentFps_timed=0; var intervalID, requestID; // Setup canvas being animated var canvas = document.getElementById("c"); var canvas_timed = document.getElementById("c2"); canvas_timed.width = canvas.width = 300; canvas_timed.height = canvas.height = 300; var ctx = canvas.getContext("2d"); var ctx2 = canvas_timed.getContext("2d"); // Setup input event handlers $fps.on('click change keyup', function() { if (this.value > 0) { fpsInterval = 1000 / +this.value; } }); $period.on('click change keyup', function() { if (this.value > 0) { if (intervalID) { clearInterval(intervalID); } intervalID = setInterval(sampleFps, +this.value); } }); function startAnimating(fps, sampleFreq) { ctx.fillStyle = ctx2.fillStyle = "#000"; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx2.fillRect(0, 0, canvas.width, canvas.height); ctx2.font = ctx.font = "32px sans"; fpsInterval = 1000 / fps; lastDrawTime = performance.now(); lastSampleTime = lastDrawTime; frameCount = 0; frameCount_timed = 0; animate(); intervalID = setInterval(sampleFps, sampleFreq); animate_timed() } function sampleFps() { // sample FPS var now = performance.now(); if (frameCount > 0) { currentFps = (frameCount / (now - lastSampleTime) * 1000).toFixed(2); currentFps_timed = (frameCount_timed / (now - lastSampleTime) * 1000).toFixed(2); $results.text(currentFps + " | " + currentFps_timed); frameCount = 0; frameCount_timed = 0; } lastSampleTime = now; } function drawNextFrame(now, canvas, ctx, fpsCount) { // Just draw an oscillating seconds-hand var length = Math.min(canvas.width, canvas.height) / 2.1; var step = 15000; var theta = (now % step) / step * 2 * Math.PI; var xCenter = canvas.width / 2; var yCenter = canvas.height / 2; var x = xCenter + length * Math.cos(theta); var y = yCenter + length * Math.sin(theta); ctx.beginPath(); ctx.moveTo(xCenter, yCenter); ctx.lineTo(x, y); ctx.fillStyle = ctx.strokeStyle = 'white'; ctx.stroke(); var theta2 = theta + 3.14/6; ctx.beginPath(); ctx.moveTo(xCenter, yCenter); ctx.lineTo(x, y); ctx.arc(xCenter, yCenter, length*2, theta, theta2); ctx.fillStyle = "rgba(0,0,0,.1)" ctx.fill(); ctx.fillStyle = "#000"; ctx.fillRect(0,0,100,30); ctx.fillStyle = "#080"; ctx.fillText(fpsCount,10,30); } // redraw second canvas each fpsInterval (1000/fps) function animate_timed() { frameCount_timed++; drawNextFrame( performance.now(), canvas_timed, ctx2, currentFps_timed); setTimeout(animate_timed, fpsInterval); } function animate(now) { // request another frame requestAnimationFrame(animate); // calc elapsed time since last loop var elapsed = now - lastDrawTime; // if enough time has elapsed, draw the next frame if (elapsed > fpsInterval) { // Get ready for next frame by setting lastDrawTime=now, but... // Also, adjust for fpsInterval not being multiple of 16.67 lastDrawTime = now - (elapsed % fpsInterval); frameCount++; drawNextFrame(now, canvas, ctx, currentFps); } } startAnimating(+$fps.val(), +$period.val());
input{ width:100px; } #tvs{ color:red; padding:0px 25px; } H3{ font-weight:400; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <h3>requestAnimationFrame skipping <span id="tvs">vs.</span> setTimeout() redraw</h3> <div> <input id="fps" type="number" value="33"/> FPS: <span id="results"></span> </div> <div> <input id="period" type="number" value="1000"/> Sample period (fps, ms) </div> <canvas id="c"></canvas><canvas id="c2"></canvas>
Kode asli oleh @tavnab.
sumber
var time = 0; var time_framerate = 1000; //in milliseconds function animate(timestamp) { if(timestamp > time + time_framerate) { time = timestamp; //your code } window.requestAnimationFrame(animate); }
sumber
Saya selalu melakukannya dengan cara yang sangat sederhana ini tanpa mengotak-atik cap waktu:
var fps, eachNthFrame, frameCount; fps = 30; //This variable specifies how many frames should be skipped. //If it is 1 then no frames are skipped. If it is 2, one frame //is skipped so "eachSecondFrame" is renderd. eachNthFrame = Math.round((1000 / fps) / 16.66); //This variable is the number of the current frame. It is set to eachNthFrame so that the //first frame will be renderd. frameCount = eachNthFrame; requestAnimationFrame(frame); //I think the rest is self-explanatory fucntion frame() { if (frameCount == eachNthFrame) { frameCount = 0; animate(); } frameCount++; requestAnimationFrame(frame); }
sumber
Solusi sederhana untuk masalah ini adalah kembali dari render loop jika frame tidak diperlukan untuk merender:
const FPS = 60; let prevTick = 0; function render() { requestAnimationFrame(render); // clamp to fixed framerate let now = Math.round(FPS * Date.now() / 1000); if (now == prevTick) return; prevTick = now; // otherwise, do your stuff ... }
Penting untuk diketahui bahwa requestAnimationFrame bergantung pada kecepatan refresh monitor pengguna (vsync). Jadi, mengandalkan requestAnimationFrame untuk kecepatan game misalnya akan membuatnya tidak dapat dimainkan di monitor 200Hz jika Anda tidak menggunakan mekanisme pengatur waktu terpisah dalam simulasi Anda.
sumber
Berikut penjelasan bagus yang saya temukan: CreativeJS.com , untuk membungkus panggilan setTimeou) di dalam fungsi yang diteruskan ke requestAnimationFrame. Perhatian saya dengan requestionAnimationFrame "biasa" adalah, "bagaimana jika saya hanya ingin animasi tiga kali per detik?" Bahkan dengan requestAnimationFrame (kebalikan dari setTimeout) adalah bahwa ia masih membuang (beberapa) jumlah "energi" (artinya bahwa kode Browser melakukan sesuatu, dan mungkin memperlambat sistem) 60 atau 120 atau berapa kali dalam satu detik, karena menentang hanya dua atau tiga kali per detik (seperti yang Anda inginkan).
Sebagian besar waktu saya menjalankan browser saya dengan JavaScript mati secara sengaja hanya karena alasan ini. Tapi, saya menggunakan Yosemite 10.10.3, dan saya pikir ada masalah pengatur waktu - setidaknya di sistem lama saya (relatif lama - artinya 2011).
sumber