Se pare că requestAnimationFrame este modalitatea de facto de a anima lucrurile acum. A funcționat destul de bine pentru mine în cea mai mare parte, dar acum încerc să fac niște animații pe pânză și mă întrebam: Există vreo modalitate de a mă asigura că rulează la un anumit fps? Înțeleg că scopul rAF-ului este pentru animații uniform și în mod constant și aș putea risca să-mi fac animația agitată, dar chiar acum pare să ruleze la viteze drastic diferite destul de arbitrar și mă întreb dacă există o modalitate de a combate că cumva.

Aș folosi setInterval, dar vreau optimizările oferite de rAF (mai ales oprirea automată când fila este focalizată).

În cazul în care cineva vrea să se uite la codul meu, este destul de mult:

În cazul în care Node.drawFlash () este doar un cod care determină raza pe baza unei variabile de contor și apoi desenează un cerc.

10 Răspunsuri 10

Cum să accelerați requestAnimationFrame la o anumită rată de cadre

Această metodă funcționează testând timpul scurs de la executarea ultimei bucle de cadre.

Codul dvs. de desen se execută numai când a trecut intervalul dvs. FPS specificat.

Prima parte a codului stabilește câteva variabile utilizate pentru a calcula timpul scurs.

Și acest cod este bucla reală RequestAnimationFrame care atrage FPS-ul specificat.

javascript

Actualizare 2016/6

Problema care limitează rata cadrelor este că ecranul are o rată de actualizare constantă, de obicei 60 FPS.

Dacă dorim 24 FPS, nu vom primi niciodată adevăratul 24 fps pe ecran, îl putem cronometra ca atare, dar nu îl putem afișa, deoarece monitorul poate afișa cadre sincronizate doar la 15 fps, 30 fps sau 60 fps (unele monitoare au și 120 fps ).

Cu toate acestea, în scopuri de sincronizare, putem calcula și actualiza când este posibil.

Puteți construi toată logica pentru controlul ratei de cadre încapsulând calcule și apeluri de apel într-un obiect:

Apoi adăugați un controler și un cod de configurare:

Utilizare

Devine foarte simplu - acum, tot ce trebuie să facem este să creăm o instanță prin setarea funcției de apel invers și a ratei de cadre dorite, astfel:

Apoi începeți (care ar putea fi comportamentul implicit dacă doriți):

Gata, toată logica este gestionată intern.

Vechi răspuns

Scopul principal al requestAnimationFrame este de a sincroniza actualizările cu rata de reîmprospătare a monitorului. Acest lucru vă va cere să animați la FPS-ul monitorului sau la un factor al acestuia (de exemplu, 60, 30, 15 FPS pentru o rată de reîmprospătare tipică @ 60 Hz).

Dacă doriți un FPS mai arbitrar, atunci nu are rost să folosiți rAF, deoarece rata cadrelor nu se va potrivi oricând cu frecvența de actualizare a monitorului (doar un cadru ici și colo), ceea ce pur și simplu nu vă poate oferi o animație lină (ca în cazul tuturor re-temporizărilor cadrelor ) și puteți utiliza în schimb setTimeout sau setInterval.

Aceasta este, de asemenea, o problemă bine cunoscută în industria video profesională atunci când doriți să redați un videoclip la un alt FPS, apoi dispozitivul care îl afișează se reîmprospătează. Au fost folosite multe tehnici, cum ar fi amestecarea cadrelor și reconstruirea cadrelor intermediare complexe bazate pe vectori de mișcare, dar cu pânza aceste tehnici nu sunt disponibile și rezultatul va fi întotdeauna video sacadat.

Motivul pentru care plasăm setTimeout mai întâi (și de ce unii plasează rAF primul când se folosește un poly-fill) este că acest lucru va fi mai precis, deoarece setTimeout va pune la coadă un eveniment imediat când începe bucla, astfel încât, indiferent de cât timp rămâne codul va fi utilizat (cu condiția să nu depășească intervalul de expirare) următorul apel va fi la intervalul pe care îl reprezintă (pentru rAF pur acest lucru nu este esențial deoarece rAF va încerca să sară pe următorul cadru în orice caz).

De asemenea, merită să rețineți că plasarea acestuia pe primul loc va risca, de asemenea, ca apelurile să se acumuleze ca și în cazul setInterval. setInterval poate fi puțin mai precis pentru această utilizare.

Și puteți utiliza setInterval în locul buclei pentru a face același lucru.

Și pentru a opri bucla:

Pentru a reduce rata cadrelor atunci când fila se estompează, puteți adăuga un factor ca acesta:

În acest fel puteți reduce FPS la 1/4 etc.

Vă sugerez să vă împachetați apelul pentru requestAnimationFrame într-un setTimeout:

Trebuie să apelați requestAnimationFrame din setTimeout, mai degrabă decât invers, deoarece requestAnimationFrame programează funcția dvs. să ruleze chiar înainte de următoarea revopsire și, dacă întârziați actualizarea în continuare folosind setTimeout, veți fi pierdut acea fereastră de timp. Cu toate acestea, a face invers este bine, deoarece pur și simplu așteptați o perioadă de timp înainte de a face cererea.

Toate acestea sunt idei bune în teorie, până când te adânci. Problema este că nu puteți accelera un RAF fără a-l sincroniza, învingând scopul său pentru a exista. Deci, lăsați-l să ruleze la viteză maximă și să vă actualizați datele într-o buclă separată, sau chiar un fir separat!

Da, am spus-o. Puteți face JavaScript cu mai multe fire în browser!

Există două metode pe care le cunosc, care funcționează extrem de bine fără smocuri, folosind mult mai puțin suc și creând mai puțină căldură. Rezultatul net este sincronizarea exactă la scară umană și eficiența mașinii.

Îmi cer scuze dacă acest lucru este puțin vorbitor, dar iată.

Metoda 1: Actualizați datele prin setInterval și grafica prin RAF.

Utilizați un set separat Interval pentru actualizarea valorilor de traducere și rotație, fizică, coliziuni etc. Păstrați aceste valori într-un obiect pentru fiecare element animat. Atribuiți șirul de transformare unei variabile din obiectul setInterval „cadru”. Păstrați aceste obiecte într-o matrice. Setați intervalul la fps-ul dorit în ms: ms = (1000/fps). Acest lucru menține un ceas constant care permite aceleași fps pe orice dispozitiv, indiferent de viteza RAF. Nu atribuiți transformările elementelor de aici!

Într-o buclă requestAnimationFrame, iterați prin matricea dvs. cu o buclă veche pentru școală - nu utilizați formularele mai noi aici, acestea sunt lente!

În funcția rafUpdate, obțineți șirul de transformare din obiectul js din matrice și id-ul elementelor sale. Ar trebui să aveți deja elementele „sprite” atașate la o variabilă sau ușor accesibile prin alte mijloace, astfel încât să nu pierdeți timp pentru a le primi în RAF. Păstrarea lor într-un obiect numit după ID-ul lor HTML funcționează destul de bine. Configurați acea parte înainte de a intra chiar în SI sau RAF.

Utilizați RAF pentru a vă actualiza numai transformările, utilizați numai transformări 3D (chiar și pentru 2d) și setați css "will-change: transform;" pe elemente care se vor schimba. Acest lucru menține transformările sincronizate cu rata de reîmprospătare nativă cât mai mult posibil, dă lovituri în GPU și îi spune browserului unde să se concentreze cel mai mult.

Deci ar trebui să aveți ceva de genul acestui pseudocod.

Aceasta păstrează actualizările obiectelor de date și transformă șirurile sincronizate cu rata de „cadre” dorită în SI, iar atribuțiile de transformare reale din RAF sincronizate cu rata de reîmprospătare GPU. Deci actualizările grafice efective sunt doar în RAF, dar modificările aduse datelor și construirea șirului de transformare se află în SI, deci nu există jankies, ci „timpul” circulă la rata de cadre dorită.

Metoda 2. Puneți SI într-un web-worker. Acesta este FAAAST și neted!

La fel ca metoda 1, dar puneți SI în web-worker. Va rula apoi pe un fir total separat, lăsând pagina să se ocupe doar de RAF și UI. Treceți matricea sprite înainte și înapoi ca „obiect transferabil”. Acesta este rapid. Nu este nevoie de timp pentru a clona sau serializa, dar nu este ca să treci prin referință prin faptul că referința din cealaltă parte este distrusă, deci va trebui să treci ambele părți în cealaltă parte și să le actualizezi doar atunci când sunt prezente, sortează ca și cum ai trece o notă înainte și înapoi cu prietena ta la liceu.

Numai unul poate citi și scrie odată. Acest lucru este în regulă atâta timp cât verifică dacă nu este nedefinit pentru a evita o eroare. RAF este RAPID și îl va retrage imediat, apoi va trece printr-o grămadă de cadre GPU doar verificând dacă a fost trimis încă înapoi. SI din web-worker va avea matricea sprite de cele mai multe ori și va actualiza datele de poziție, mișcare și fizică, precum și crearea noului șir de transformare, apoi îl va transmite înapoi la RAF în pagină.

Acesta este cel mai rapid mod pe care îl cunosc de a anima elemente prin script. Cele două funcții vor rula ca două programe separate, pe două fire separate, profitând de procesoarele multi-core într-un mod în care un singur script js nu. Animație javascript cu mai multe fire.

Și o va face fără probleme, dar la ritmul de cadre specificat, cu foarte puține divergențe.

Oricare dintre aceste două metode vă va asigura că scriptul dvs. va rula la aceeași viteză pe orice computer, telefon, tabletă etc. (desigur, în limitele capacității dispozitivului și browserului).