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 ...
var element = [
mario: document.getElementById('mario'),
luigi: document.getElementById('luigi')
]
var sprite = [
mario: { id: mario ....physics data, id, and updated transform string (from SI) here },
luigi: { id: luigi .....same }
]
var SIupdate = function(object){
object.pos.x += object.mov.pos.x;
object.transform =
'translate3d('+
object.pos.x+','+
object.pos.y+','+
object.pos.z+
') '+
'rotationZ('+object.rot.z+') '+
'rotationY('+object.rot.y+') '+
'rotationX('+object.rot.x+') '+
'scale3d('.... if desired
;
}
var fps = 30;
setInterval(function(){
for(var i=0; i<sprite.length-1; i++){ SIupdate(sprite[i]); }
},1000/fps);
var rAf = function(){
for(var i=0; i<sprite.length-1; i++){ rAF.update(sprite[i]) }
window.requestAnimationFrame(rAF);
}
rAF.update = function(object){
if(object.old_transform !== object.transform){
element[object.id].style.transform = transform;
object.old_transform = object.transform;
}
}
window.requestAnimationFrame(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).
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.