To wszystko są dobre pomysły w teorii, dopóki nie wejdziesz głęboko. Problem polega na tym, że nie można dławić RAF bez dez-synchronizacji, pokonując jego cel istnienia. Więc pozwalasz mu działać z pełną prędkością i aktualizujesz dane w osobnej pętli , a nawet w osobnym wątku!
Tak, powiedziałem to. W przeglądarce możesz wykonywać wielowątkowy JavaScript!
Wiem, że są dwie metody, które działają wyjątkowo dobrze bez szarpania, zużywając znacznie mniej soku i wytwarzając mniej ciepła. Dokładne wyczucie czasu na skalę ludzką i wydajność maszyny to wynik netto.
Przepraszamy, jeśli jest to trochę rozwlekłe, ale proszę bardzo ...
Metoda 1: Zaktualizuj dane za pomocą setInterval i grafiki za pomocą RAF.
Użyj osobnego setInterval do aktualizacji wartości przesunięcia i obrotu, fizyki, kolizji itp. Zachowaj te wartości w obiekcie dla każdego animowanego elementu. Przypisz ciąg transformacji do zmiennej w obiekcie w każdej „ramce” setInterval. Zachowaj te obiekty w tablicy. Ustaw interwał na żądane fps w ms: ms = (1000 / fps). Utrzymuje to stały zegar, który pozwala na taką samą liczbę klatek na sekundę na dowolnym urządzeniu, niezależnie od prędkości RAF. Nie przypisuj tutaj transformacji do elementów!
W pętli requestAnimationFrame przeprowadź iterację przez tablicę za pomocą starej pętli for - nie używaj tutaj nowszych formularzy, są one powolne!
for(var i=0; i<sprite.length-1; i++){ rafUpdate(sprite[i]); }
W swojej funkcji rafUpdate pobierz łańcuch transformacji z obiektu js w tablicy oraz identyfikator jego elementów. Powinieneś już mieć swoje elementy „sprite” dołączone do zmiennej lub łatwo dostępne w inny sposób, aby nie tracić czasu na „zdobywanie” ich w RAF. Przechowywanie ich w obiekcie nazwanym na podstawie ich identyfikatorów HTML działa całkiem nieźle. Ustaw tę część, zanim trafi do twojego SI lub RAF.
Użyj RAF zaktualizować przekształca tylko używać wyłącznie 3D przekształca (nawet dla 2d) i ustaw css „will change: transform”; na elementach, które się zmienią. Dzięki temu Twoje transformacje są zsynchronizowane z natywną częstotliwością odświeżania tak bardzo, jak to możliwe, uruchamia GPU i informuje przeglądarkę, na czym się najbardziej skoncentrować.
Więc powinieneś mieć coś takiego jak ten pseudokod ...
// 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
Dzięki temu aktualizacje obiektów danych i ciągi transformacji są zsynchronizowane z żądaną częstotliwością klatek w SI, a rzeczywiste przypisania transformacji w RAF są zsynchronizowane z częstotliwością odświeżania GPU. Tak więc rzeczywiste aktualizacje grafiki są tylko w RAF, ale zmiany danych i tworzenie ciągu transformacji są w SI, więc nie ma jankie, ale „czas” płynie z pożądaną liczbą klatek na sekundę.
Pływ:
[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]
Metoda 2. Umieść SI w pracowniku sieciowym. Ten jest FAAAST i gładki!
To samo co metoda 1, ale umieść SI w programie roboczym sieci. Będzie wtedy działać w zupełnie osobnym wątku, pozostawiając stronę tylko do obsługi RAF i interfejsu użytkownika. Przekazuj tablicę duszków w tę iz powrotem jako „przenoszony obiekt”. To jest szybkie. Klonowanie lub serializacja nie zajmuje czasu, ale to nie jest tak, jak przekazywanie przez referencję, ponieważ referencja z drugiej strony jest niszczona, więc musisz mieć obie strony przejść na drugą stronę i zaktualizować je tylko wtedy, gdy są obecne, posortuj jak przekazywanie sobie notatki z dziewczyną w liceum.
Jednocześnie tylko jeden może czytać i pisać. Jest to w porządku, o ile sprawdzają, czy nie jest to nieokreślone, aby uniknąć błędu. RAF jest SZYBKI i natychmiast go odkopie, a następnie przejdzie przez kilka ramek GPU, sprawdzając tylko, czy został jeszcze odesłany. SI w programie roboczym sieciowym będzie przez większość czasu mieć tablicę duszków i będzie aktualizować dane dotyczące pozycji, ruchu i fizyki, a także tworzyć nowy ciąg transformacji, a następnie przekazywać go z powrotem do RAF na stronie.
Jest to najszybszy znany mi sposób animowania elementów za pomocą skryptu. Te dwie funkcje będą działały jako dwa oddzielne programy, w dwóch oddzielnych wątkach, wykorzystując wielordzeniowe procesory w sposób, w jaki nie robi tego pojedynczy skrypt js. Wielowątkowa animacja javascript.
I zrobi to płynnie bez szarpnięć, ale przy rzeczywistej określonej liczbie klatek na sekundę, z bardzo małą rozbieżnością.
Wynik:
Każda z tych dwóch metod zapewni, że skrypt będzie działał z taką samą prędkością na dowolnym komputerze, telefonie, tablecie itp. (Oczywiście w ramach możliwości urządzenia i przeglądarki).
requestAnimationFrame
jest (jak sugeruje nazwa) żądanie ramki animacji tylko wtedy, gdy jest to potrzebne. Powiedzmy, że pokazujesz statyczne czarne płótno, powinieneś uzyskać 0 fps, ponieważ nie jest potrzebna nowa klatka. Ale jeśli wyświetlasz animację wymagającą 60 klatek na sekundę, to też powinieneś otrzymać.rAF
pozwala po prostu „pomijać” bezużyteczne ramki i oszczędzać procesor.