Bunların hepsi, siz derinlere inene kadar teoride iyi fikirlerdir. Sorun şu ki, bir RAF'ı senkronize etmeden kısıtlayamazsınız, var olanın amacını yenersiniz. Böylece tam hızda çalışmasına izin verin ve verilerinizi ayrı bir döngüde , hatta ayrı bir iş parçacığında güncelleyin!
Evet, söyledim. Sen edebilirsiniz tarayıcıda çok dişli JavaScript yap!
Jank olmadan son derece iyi çalıştığını, çok daha az meyve suyu kullandığını ve daha az ısı ürettiğini bildiğim iki yöntem var. Doğru insan ölçeği zamanlaması ve makine verimliliği net sonuçtur.
Bu biraz garip olsa özür dileriz, ama işte gidiyor ...
Yöntem 1: setInterval yoluyla verileri ve RAF aracılığıyla grafikleri güncelleyin.
Çeviri ve döndürme değerlerini, fizik, çarpışma, vb. Güncellemek için ayrı bir setInterval kullanın. Bu değerleri her animasyonlu öğe için bir nesnede tutun. Dönüştürme dizesini her setInterval 'frame' nesnesindeki bir değişkene atayın. Bu nesneleri bir dizide saklayın. Aralıkınızı istediğiniz fps değerine ms: ms = (1000 / fps) olarak ayarlayın. Bu, RAF hızından bağımsız olarak herhangi bir cihazda aynı fps'ye izin veren sabit bir saat tutar. Buradaki öğelere dönüşümleri atamayın!
Bir requestAnimationFrame döngüsünde, döngü için eski bir okulla dizinizi yineleyin - burada daha yeni formları kullanmayın, yavaşlar!
for(var i=0; i<sprite.length-1; i++){ rafUpdate(sprite[i]); }
RafUpdate işlevinizde, dizedeki js nesnenizden ve dönüştürme öğelerinden id dönüştürme dizesini alın. 'Sprite' öğelerinizi bir değişkene iliştirilmiş veya başka yollarla kolayca erişilebilir hale getirmelisiniz, böylece bunları RAF'a 'almak' için zaman kaybetmezsiniz. Onları html kimlikleri adlı bir nesnede tutmak oldukça iyi çalışıyor. SI veya RAF'nıza girmeden önce bu parçayı yerleştirin.
RAF'ı yalnızca dönüşümlerinizi güncellemek için kullanın , yalnızca 3B dönüşümleri (2d için bile) kullanın ve css "will-change: transform;" değişecek öğeler üzerinde. Bu, dönüşümlerinizi yerel yenileme hızıyla mümkün olduğunca senkronize tutar, GPU'yu başlatır ve tarayıcıya en çok nereye konsantre olacağını söyler.
Yani bu sahte kod gibi bir şey olmalı ...
// 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
Bu, SI'daki istenen 'kare' hızıyla senkronize edilen veri nesneleri ve dönüştürme dizelerini ve RAF'taki GPU yenileme oranıyla senkronize edilen gerçek dönüşüm atamalarını günceller. Dolayısıyla, gerçek grafik güncellemeleri sadece RAF'tadır, ancak verilerdeki değişiklikler ve dönüştürme dizesinin oluşturulması SI'dadır, bu nedenle janki yok, ancak 'kare' istenen kare hızında akar.
Akış:
[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]
Yöntem 2. SI bir web çalışanı koyun. Bu FAAAST ve pürüzsüz!
Yöntem 1 ile aynı, ancak SI web çalışanı koyun. Tamamen ayrı bir iş parçacığında çalışacak ve sayfa yalnızca RAF ve kullanıcı arayüzüyle ilgilenecek. Hareketli grafik dizisini 'aktarılabilir nesne' olarak ileri ve geri aktarın. Bu buko hızlı. Klonlamak veya serileştirmek zaman almaz, ancak diğer taraftan referansın yok edilmesi nedeniyle referansla geçmek gibi değildir, bu nedenle her iki tarafın diğer tarafa geçmesine ve yalnızca mevcut olduğunda güncellemenize, sıralamanıza lisede kız arkadaşınızla bir not ileri geri geçmek gibi.
Aynı anda yalnızca biri okuyabilir ve yazabilir. Bir hatayı önlemek için tanımlanıp tanımlanmadığını kontrol ettikleri sürece bu iyidir. RAF HIZLI ve hemen geri dönecek, daha sonra geri gönderilip gönderilmediğini kontrol eden bir grup GPU çerçevesinden geçecek. Web çalışanındaki SI çoğu zaman sprite dizisine sahip olacak ve yeni dönüşüm dizesini oluşturmanın yanı sıra konum, hareket ve fizik verilerini güncelleyecek ve ardından sayfadaki RAF'a geri gönderecektir.
Bu, komut dosyası aracılığıyla öğeleri canlandırmayı bildiğim en hızlı yoldur. İki işlev tek bir js betiğinin çalışmadığı şekilde çok çekirdekli CPU'lardan yararlanarak iki ayrı iş parçacığında iki ayrı program olarak çalışacaktır. Çok iş parçacıklı javascript animasyonu.
Ve bunu sorunsuz bir şekilde yapacaktır, ancak gerçek belirtilen kare hızında, çok az sapma ile.
Sonuç:
Bu iki yöntemden herhangi biri, komut dosyanızın herhangi bir PC, telefon, tablet vb. Üzerinde aynı hızda çalışmasını sağlayacaktır (elbette cihazın ve tarayıcının yetenekleri dahilinde).
requestAnimationFrame
(adından da anlaşılacağı gibi) sadece gerektiğinde bir animasyon karesi istemektir. Diyelim ki statik bir siyah kanvas gösterdiğinizde, yeni kare gerekmediğinden 0 fps almalısınız. Ancak 60 fps gerektiren bir animasyon görüntülüyorsanız, bunu da almalısınız.rAF
sadece işe yaramaz çerçeveleri "atlamak" ve daha sonra CPU kaydetmek için izin verir.