To wygląda jak requestAnimationFrame
to, że jest to de facto sposób na animowanie rzeczy teraz. W większości przypadków działało to całkiem dobrze, ale teraz próbuję zrobić kilka animacji na płótnie i zastanawiałem się: czy jest jakiś sposób, aby upewnić się, że działa z określoną liczbą klatek na sekundę? Rozumiem, że celem RAF jest konsekwentnie płynne animacje i mogę ryzykować, że moja animacja będzie się zacinać, ale w tej chwili wydaje się, że działa z drastycznie różnymi prędkościami dość arbitralnie i zastanawiam się, czy istnieje sposób na walkę że jakoś.
Używałbym, setInterval
ale chcę optymalizacji, które oferuje rAF (szczególnie automatyczne zatrzymywanie, gdy zakładka jest aktywna).
Na wypadek, gdyby ktoś chciał spojrzeć na mój kod, jest to mniej więcej:
animateFlash: function() {
ctx_fg.clearRect(0,0,canvasWidth,canvasHeight);
ctx_fg.fillStyle = 'rgba(177,39,116,1)';
ctx_fg.strokeStyle = 'none';
ctx_fg.beginPath();
for(var i in nodes) {
nodes[i].drawFlash();
}
ctx_fg.fill();
ctx_fg.closePath();
var instance = this;
var rafID = requestAnimationFrame(function(){
instance.animateFlash();
})
var unfinishedNodes = nodes.filter(function(elem){
return elem.timer < timerMax;
});
if(unfinishedNodes.length === 0) {
console.log("done");
cancelAnimationFrame(rafID);
instance.animate();
}
}
Gdzie Node.drawFlash () to tylko jakiś kod, który określa promień na podstawie zmiennej licznika, a następnie rysuje okrąg.
źródło
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.Odpowiedzi:
Jak ograniczyć requestAnimationFrame do określonej liczby klatek na sekundę
Demo dławienie przy 5 FPS: http://jsfiddle.net/m1erickson/CtsY3/
Ta metoda działa poprzez testowanie czasu, który upłynął od wykonania pętli ostatniej ramki.
Twój kod rysowania jest wykonywany tylko wtedy, gdy upłynął określony interwał FPS.
Pierwsza część kodu ustawia niektóre zmienne używane do obliczania czasu, który upłynął.
Ten kod jest rzeczywistą pętlą requestAnimationFrame, która rysuje z określoną liczbą klatek na sekundę.
źródło
Aktualizacja 2016/6
Problem ograniczający liczbę klatek na sekundę polega na tym, że ekran ma stałą częstotliwość odświeżania, zwykle 60 klatek na sekundę.
Jeśli chcemy 24 kl./s, nigdy nie uzyskamy prawdziwych 24 kl./s na ekranie, możemy to tak ustawić, ale nie pokazać, ponieważ monitor może wyświetlać tylko zsynchronizowane klatki przy 15 fps, 30 fps lub 60 fps (niektóre monitory również 120 fps ).
Jednak ze względu na czas możemy obliczyć i zaktualizować, gdy to możliwe.
Możesz zbudować całą logikę kontrolowania liczby klatek na sekundę poprzez hermetyzację obliczeń i wywołań zwrotnych w obiekcie:
Następnie dodaj kontroler i kod konfiguracyjny:
Stosowanie
Staje się to bardzo proste - teraz wszystko, co musimy zrobić, to utworzyć instancję, ustawiając funkcję zwrotną i żądaną liczbę klatek na sekundę, tak jak poniżej:
Następnie uruchom (co może być zachowaniem domyślnym w razie potrzeby):
To wszystko, cała logika jest obsługiwana wewnętrznie.
Próbny
Stara odpowiedź
Głównym celem
requestAnimationFrame
jest synchronizacja aktualizacji z częstotliwością odświeżania monitora. Będzie to wymagało animacji przy FPS monitora lub jej współczynniku (tj. 60, 30, 15 FPS dla typowej częstotliwości odświeżania przy 60 Hz).Jeśli chcesz mieć bardziej arbitralny FPS, nie ma sensu używać rAF, ponieważ częstotliwość klatek i tak nigdy nie będzie pasować do częstotliwości aktualizacji monitora (tylko klatka tu i tam), co po prostu nie może zapewnić płynnej animacji (jak w przypadku wszystkich zmian czasu klatek) ) i równie dobrze możesz użyć
setTimeout
lubsetInterval
zamiast tego.Jest to również dobrze znany problem w branży profesjonalnego wideo, gdy chcesz odtwarzać wideo z inną liczbą klatek na sekundę niż urządzenie wyświetlające je odświeża się. Zastosowano wiele technik, takich jak łączenie klatek i złożone ponowne tworzenie klatek pośrednich w oparciu o wektory ruchu, ale w przypadku płótna te techniki nie są dostępne, a rezultatem będzie zawsze urywany obraz.
Powodem, dla którego umieszczamy
setTimeout
pierwsze miejsce (i dlaczego jakieś miejsce narAF
pierwszym miejscu, gdy jest używane wypełnienie poly), jest to, że będzie to dokładniejsze, ponieważsetTimeout
ustawia zdarzenie w kolejce natychmiast po rozpoczęciu pętli, więc bez względu na to, ile czasu zajmie pozostały kod (pod warunkiem, że nie przekracza limitu czasu), następne wywołanie będzie odbywać się w przedziale, który reprezentuje (dla czystego rAF nie jest to konieczne, ponieważ rAF będzie próbował przeskoczyć do następnej klatki w każdym przypadku).Warto również zauważyć, że umieszczenie go na pierwszym miejscu może również spowodować kumulowanie się połączeń, tak jak w przypadku
setInterval
.setInterval
może być nieco dokładniejsze w tym zastosowaniu.setInterval
Zamiast tego możesz użyć poza pętlą, aby zrobić to samo.Aby zatrzymać pętlę:
Aby zmniejszyć liczbę klatek na sekundę, gdy karta jest rozmyta, możesz dodać taki czynnik:
W ten sposób możesz zmniejszyć FPS do 1/4 itd.
źródło
requestAnimationFrame
jest synchronizacja operacji DOM (odczyt / zapis), więc nieużywanie go obniży wydajność podczas uzyskiwania dostępu do DOM, ponieważ operacje nie będą ustawiane w kolejce do wykonania razem i wymuszą niepotrzebne odświeżenie układu.Proponuję zawrzeć połączenie do
requestAnimationFrame
plikusetTimeout
. Jeśli wywołujeszsetTimeout
z funkcji, z której zażądałeś ramki animacji, pokonujesz celrequestAnimationFrame
. Ale jeśli dzwoniszrequestAnimationFrame
z wewnątrzsetTimeout
, działa to płynnie:źródło
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!
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 ...
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:
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).
źródło
visibilitychange
wydarzenie.Jak łatwo ustawić przepustnicę na konkretny FPS:
Źródło: Izaak Sukin - szczegółowe wyjaśnienie pętli gier JavaScript i ich synchronizacji
źródło
Pominięcie requestAnimationFrame powoduje, że animacja nie jest płynna (pożądana) przy niestandardowej liczbie klatek na sekundę.
Oryginalny kod @tavnab.
źródło
źródło
Zawsze robię to w bardzo prosty sposób bez majstrowania przy sygnaturach czasowych:
źródło
Oto dobre wyjaśnienie, które znalazłem: CreativeJS.com , aby opakować wywołanie setTimeou) wewnątrz funkcji przekazanej do requestAnimationFrame. Moim zmartwieniem w przypadku „zwykłego” requestionAnimationFrame byłoby: „a co, jeśli chcę , aby animowała się tylko trzy razy na sekundę?” Nawet w przypadku requestAnimationFrame (w przeciwieństwie do setTimeout) nadal marnuje (pewną) ilość „energii” (co oznacza, że kod przeglądarki coś robi i prawdopodobnie spowalnia system) 60 lub 120, a nawet wiele razy na sekundę, w przeciwieństwie do dwóch lub trzech razy na sekundę (jak chcesz).
Przez większość czasu używam przeglądarek z wyłączonym JavaScriptem właśnie z tego powodu. Ale używam Yosemite 10.10.3 i myślę, że jest z nim jakiś problem z zegarem - przynajmniej w moim starym systemie (stosunkowo starym - czyli 2011).
źródło