Das sind alles gute Ideen in der Theorie, bis Sie tief gehen. Das Problem ist, dass Sie eine RAF nicht drosseln können, ohne sie zu de-synchronisieren, wodurch ihr eigentlicher Zweck für die Existenz zunichte gemacht wird. So können Sie es bei voller Geschwindigkeit laufen lassen und Ihre Daten in einer separaten Schleife aktualisieren , oder sogar einem separaten Thread!
Ja, ich habe es gesagt. Sie können im Browser Multithread-JavaScript erstellen!
Ich kenne zwei Methoden, die ohne Ruck sehr gut funktionieren, viel weniger Saft verbrauchen und weniger Wärme erzeugen. Genaue Zeitmessung im menschlichen Maßstab und Maschineneffizienz sind das Nettoergebnis.
Entschuldigung, wenn dies ein wenig wortreich ist, aber hier geht ...
Methode 1: Aktualisieren Sie Daten über setInterval und Grafiken über RAF.
Verwenden Sie ein separates setInterval zum Aktualisieren von Übersetzungs- und Rotationswerten, Physik, Kollisionen usw. Behalten Sie diese Werte für jedes animierte Element in einem Objekt. Weisen Sie die Transformationszeichenfolge einer Variablen im Objekt jedes setInterval-Frames zu. Halten Sie diese Objekte in einem Array. Stellen Sie Ihr Intervall in ms auf die gewünschten fps ein: ms = (1000 / fps). Dadurch bleibt eine konstante Uhr erhalten, die auf jedem Gerät unabhängig von der RAF-Geschwindigkeit die gleichen Bilder pro Sekunde zulässt. Ordnen Sie die Transformationen hier nicht den Elementen zu!
Durchlaufen Sie in einer requestAnimationFrame-Schleife Ihr Array mit einer for-Schleife der alten Schule - verwenden Sie hier nicht die neueren Formulare, sie sind langsam!
for(var i=0; i<sprite.length-1; i++){ rafUpdate(sprite[i]); }
Rufen Sie in Ihrer Funktion rafUpdate die Transformationszeichenfolge von Ihrem js-Objekt im Array und dessen Element-ID ab. Sie sollten Ihre Sprite-Elemente bereits an eine Variable angehängt haben oder auf andere Weise leicht zugänglich sein, damit Sie keine Zeit verlieren, sie in der RAF abzurufen. Es funktioniert ziemlich gut, sie in einem Objekt zu behalten, das nach der HTML-ID benannt ist. Richten Sie diesen Teil ein, bevor er überhaupt in Ihre SI oder RAF gelangt.
Verwenden Sie die RAF Ihre Transformationen aktualisieren nur , verwenden Sie nur 3D - Transformationen (auch für 2d) und Set css „will-change: verwandeln;“ auf Elemente, die sich ändern werden. Auf diese Weise werden Ihre Transformationen so weit wie möglich mit der nativen Aktualisierungsrate synchronisiert, die GPU aktiviert und dem Browser mitgeteilt, wo er sich am meisten konzentrieren soll.
Sie sollten also so etwas wie diesen Pseudocode haben ...
// 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
Dadurch bleiben Ihre Aktualisierungen der Datenobjekte und Transformationszeichenfolgen mit der gewünschten Framerate im SI synchronisiert, und die tatsächlichen Transformationszuweisungen in der RAF werden mit der GPU-Aktualisierungsrate synchronisiert. Die eigentlichen Grafikaktualisierungen befinden sich also nur in der RAF, aber die Änderungen an den Daten und das Erstellen der Transformationszeichenfolge befinden sich in der SI, sodass keine Ruckler, sondern "Zeit" mit der gewünschten Bildrate fließt.
Fließen:
[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]
Methode 2. Legen Sie den SI in einen Web-Worker. Dieser ist FAAAST und glatt!
Wie Methode 1, jedoch den SI in Web-Worker einfügen. Es wird dann in einem völlig separaten Thread ausgeführt, sodass die Seite nur für RAF und Benutzeroberfläche verwendet werden kann. Übergeben Sie das Sprite-Array als übertragbares Objekt hin und her. Das ist buko schnell. Das Klonen oder Serialisieren dauert nicht lange, aber es ist nicht so, als würde eine Referenz übergeben, da die Referenz von der anderen Seite zerstört wird. Sie müssen also beide Seiten auf die andere Seite übergeben und sie nur aktualisieren, wenn sie vorhanden sind, sortieren als würde man mit seiner Freundin in der High School eine Notiz hin und her geben.
Es kann immer nur einer lesen und schreiben. Dies ist in Ordnung, solange sie prüfen, ob es nicht undefiniert ist, um einen Fehler zu vermeiden. Die RAF ist SCHNELL und wird sie sofort zurückwerfen und dann eine Reihe von GPU-Frames durchlaufen, um zu überprüfen, ob sie bereits zurückgesendet wurde. Der SI im Web-Worker verfügt die meiste Zeit über das Sprite-Array und aktualisiert Positions-, Bewegungs- und Physikdaten sowie die neue Transformationszeichenfolge und gibt sie dann an die RAF auf der Seite zurück.
Dies ist der schnellste Weg, um Elemente per Skript zu animieren. Die beiden Funktionen werden als zwei separate Programme auf zwei separaten Threads ausgeführt, wobei Multi-Core-CPUs auf eine Weise genutzt werden, die ein einzelnes js-Skript nicht bietet. Multithread-Javascript-Animation.
Und das ohne Ruck, aber mit der tatsächlich angegebenen Bildrate und mit sehr geringer Abweichung.
Ergebnis:
Mit beiden Methoden wird sichergestellt, dass Ihr Skript auf jedem PC, Telefon, Tablet usw. mit der gleichen Geschwindigkeit ausgeführt wird (natürlich im Rahmen der Funktionen des Geräts und des Browsers).
requestAnimationFrame
ist (wie der Name schon sagt), dass ein Animationsrahmen nur dann angefordert wird, wenn er benötigt wird. Angenommen, Sie zeigen eine statische schwarze Leinwand. Sie sollten 0 fps erhalten, da kein neuer Frame benötigt wird. Wenn Sie jedoch eine Animation anzeigen, die 60 fps erfordert, sollten Sie diese auch erhalten.rAF
erlaubt nur, nutzlose Frames zu "überspringen" und dann die CPU zu sparen.