Każda symulacja na tej stronie używa pętli
requestAnimationFrame (rAF). Powody wykraczają poza
„bo to nowoczesny sposób" — istnieją konkretne, mierzalne różnice
wpływające na poprawność, wydajność i doświadczenie użytkownika.
Co dokładnie jest nie tak z setInterval?
setInterval(fn, 16) wywołuje funkcję co 16 ms
niezależnie od tego, czy przeglądarka jest gotowa do renderowania.
Powoduje to trzy problemy:
- Brak synchronizacji z vsync. Kompozytor przeglądarki maluje z częstotliwością 60/90/120 Hz (zależnie od obsługiwanej przez ekran). setInterval wywołuje się niezależnie, powodując rozrywanie obrazu i zmarnowaną pracę, gdy aktualizacje zdarzają się między cyklami malowania.
- Działa w ukrytych kartach. Gdy użytkownik przełączy się na inną kartę, setInterval nadal działa — zużywając CPU, drenując baterię i gromadząc czas symulacji, który uruchamia się wszystek naraz, gdy karta znów staje się widoczna.
- Dryf timera. Timery JavaScript nie działają w czasie rzeczywistym. Mogą być opóźniane przez pauzy garbage collectora, pracę układu strony lub opóźnienia obsługi wejścia. Interwał 16 ms może w praktyce wywoływać się co 16, 17, 14, 22 ms.
Co daje requestAnimationFrame
Wywołania zwrotne rAF otrzymują parametr
DOMHighResTimeStamp z (zazwyczaj) precyzją poniżej
milisekundy. Wywołanie zwrotne uruchamia się z częstotliwością
odświeżania ekranu — przeglądarka planuje je tuż przed kolejnym
malowaniem, w synchronizacji z kompozytorem.
- Automatyczne dławienie. Gdy karta jest w tle, rAF automatycznie się wstrzymuje. W niektórych przeglądarkach spada do ~1fps, aby utrzymać timery przy życiu, ale nie zużywa znaczącego CPU.
- Wyrównanie z vsync. Wywołanie zwrotne uruchamia się we właściwym momencie potoku renderowania przeglądarki: po układzie strony, przed kompozycją. Mutacje wewnątrz rAF nie wymuszają dodatkowych obliczeń układu.
-
Dokładny czas delta. Ponieważ znacznik czasu
jest dostarczany, możesz obliczyć
dt = t - prevTi mnożyć przez niego wszystkie aktualizacje fizyki. To sprawia, że fizyka jest niezależna od liczby klatek.
Poprawny wzorzec
let prevTime = performance.now();
function loop(time) {
const dt = Math.min((time - prevTime) / 1000, 0.05); // sekundy, ograniczone do 50ms
prevTime = time;
update(dt); // krok fizyki — mnożenie przyspieszeń przez dt, prędkości przez dt
render(); // rysowanie
requestAnimationFrame(loop);
}
requestAnimationFrame(loop); // start
Ograniczenie Math.min(..., 0.05) jest ważne: jeśli
karta była ukryta i wróciła, dt może wynosić kilka
sekund, powodując teleportację obiektów. Ograniczenie limituje
maksymalny krok fizyki do 50 ms, by krótkie opóźnienie nie
wysadziło symulacji.
Stały krok czasowy (dla deterministycznej fizyki)
Dla stabilnej fizyki (wykrywanie kolizji, dynamika sprężyn)
zmienny dt może powodować niestabilność przy niskim
FPS. Użyj akumulatora ze stałym krokiem czasowym:
const FIXED_DT = 1 / 60; // fizyka 60Hz
let accumulator = 0;
function loop(time) {
const frameTime = Math.min((time - prevTime) / 1000, 0.1);
prevTime = time;
accumulator += frameTime;
while (accumulator >= FIXED_DT) {
physicsStep(FIXED_DT);
accumulator -= FIXED_DT;
}
render();
requestAnimationFrame(loop);
}
Wykonuje to tyle kroków fizyki, ile potrzeba, by „nadgonić" czas
rzeczywisty, przy czym każdy krok wynosi dokładnie
1/60 s. Renderowanie może wystąpić między krokami —
użyj pozostałej frakcji accumulator / FIXED_DT, by
interpolować stan renderowania dla płynnej wizualizacji nawet
przy 120 Hz.
✅ requestAnimationFrame
- Wyrównany z vsync, bez rozrywania obrazu
- Automatycznie wstrzymuje się w ukrytych kartach
- Dokładny znacznik czasu wysokiej rozdzielczości
- Zoptymalizowane planowanie przeglądarki
- Zużywa mniej CPU, gdy ukryty
❌ setInterval
- Brak wyrównania z vsync
- Działa w ukrytych kartach (drenaż baterii)
- Dryf timera pod obciążeniem
- Brak wbudowanego czasu delta
- Może powodować „spiralę śmierci" przy nadganianiu
Zasada: nigdy nie używaj setInterval
do animacji. Użyj requestAnimationFrame z
akumulatorem czasu delta dla renderowania o zmiennej
częstotliwości i dodaj wewnętrzną pętlę ze stałym krokiem
czasowym dla stabilności fizyki.