requestAnimationFrame()
Planuje wywołanie funkcji tuż przed kolejnym odświeżeniem ekranu. Służy do płynnych animacji sterowanych JavaScriptem.
- Zwraca
- Liczbę, czyli identyfikator zaplanowanego wywołania.
Przykład
#const bar = document.querySelector('#bar')
const duration = 600
let start
function step(timestamp) {
if (start === undefined) start = timestamp
const progress = Math.min((timestamp - start) / duration, 1)
bar.style.width = `${progress * 100}%`
if (progress < 1) {
requestAnimationFrame(step)
} else {
console.log('Pasek wypełniony:', bar.style.width)
}
}
requestAnimationFrame(step)
console.log('Animacja zaplanowana')Postęp jest liczony z upływu czasu, więc pasek wypełnia się w 0,6 s bez względu na liczbę klatek na sekundę. Kliknij „Uruchom ponownie”, żeby zobaczyć animację jeszcze raz.
Definicja i zastosowanie
#Funkcja requestAnimationFrame() prosi przeglądarkę, żeby wywołała podaną funkcję przed narysowaniem następnej klatki. Zwykle dzieje się to 60 razy na sekundę, a na ekranach o wyższym odświeżaniu częściej. Zmiany wyglądu trafiają wtedy na ekran dokładnie w chwili, gdy przeglądarka i tak rysuje stronę, więc animacja jest płynniejsza niż przy setInterval().
Funkcja zwrotna wykonuje się tylko raz. Żeby zbudować animację, pod koniec każdej klatki zaplanuj następną, wywołując requestAnimationFrame() ponownie. Funkcja dostaje znacznik czasu w milisekundach (w tej samej skali co performance.now()), dzięki któremu obliczysz postęp z upływu czasu, a nie z liczby klatek. Animacja trwa wtedy tyle samo na każdym ekranie.
Wywołanie zwraca identyfikator, który przekazany do cancelAnimationFrame() anuluje zaplanowaną klatkę. W kartach działających w tle i w niewidocznych ramkach przeglądarka wstrzymuje wywołania, co oszczędza baterię. Proste przejścia i animacje lepiej opisać w CSS (transition, @keyframes), a requestAnimationFrame() zostawić dla ruchu zależnego od obliczeń, np. w grach i wykresach.
Składnia
#requestAnimationFrame(callback)
cancelAnimationFrame(requestId)Parametry
#callback
function (timestamp)
Funkcja wywoływana przed następną klatką. Dostaje znacznik czasu w milisekundach.requestId
liczba
Identyfikator zwrócony przezrequestAnimationFrame(), przekazywany docancelAnimationFrame().
Więcej przykładów
#const points = document.querySelector('#points')
const target = 150
let value = 0
function tick() {
value = Math.min(value + 30, target)
points.textContent = value
console.log(value)
if (value < target) requestAnimationFrame(tick)
}
requestAnimationFrame(tick)Każda klatka zwiększa licznik o 30, więc animacja kończy się po pięciu klatkach, czyli przy ekranie 60 Hz po około 80 ms. Na ekranie 120 Hz ten sam kod działa dwa razy szybciej, dlatego w dłuższych animacjach krok liczy się z upływu czasu, jak w pierwszym przykładzie.
const tile = document.querySelector('#tile')
const moveId = requestAnimationFrame(() => {
tile.style.transform = 'translateX(200px)'
console.log('Przesunięto kafelek')
})
requestAnimationFrame(() => {
console.log('Klatka gotowa, przesunięcie:', tile.style.transform || 'brak')
})
cancelAnimationFrame(moveId)
console.log('Identyfikator klatki to', typeof moveId)Pierwsza zaplanowana funkcja została anulowana przed najbliższą klatką, więc kafelek się nie przesunął. Druga wykonała się normalnie.
const header = document.querySelector('header')
let scheduled = false
window.addEventListener('scroll', () => {
if (scheduled) return
scheduled = true
requestAnimationFrame(() => {
header.classList.toggle('with-shadow', window.scrollY > 0)
scheduled = false
})
})Zdarzenie scroll potrafi pojawić się kilka razy w ciągu jednej klatki. Flaga scheduled sprawia, że wygląd nagłówka jest aktualizowany najwyżej raz na klatkę, tuż przed rysowaniem.
Obsługa przeglądarek
#Szeroko dostępne
Działa we wszystkich nowoczesnych przeglądarkach, także na telefonach. Możesz używać bez obaw.
- Chrome
- Edge
- Firefox
- Safari
Dobre praktyki
#- Licz postęp animacji z upływu czasu (
timestamp), a nie z liczby klatek. Na ekranie 120 Hz klatek jest dwa razy więcej niż na 60 Hz. - Każde wywołanie planuje tylko jedną klatkę. Jeśli animacja ma trwać dalej, wywołaj
requestAnimationFrame()ponownie wewnątrz funkcji. - Proste przejścia opisz w CSS przez
transitionalbo@keyframes. Przeglądarka animuje je wydajniej i bez kodu JavaScript.
Powiązane hasła
#- setTimeout()Uruchamia funkcję jeden raz, po upływie podanej liczby milisekund.
- setInterval()Wywołuje funkcję cyklicznie, co podany odstęp czasu, aż do zatrzymania przez clearInterval().
- HTMLElement.styleObiekt stylów wpisanych bezpośrednio w element (inline), do odczytu i zmiany pojedynczych właściwości CSS.
- ResizeObserverWywołuje funkcję za każdym razem, gdy zmieni się rozmiar obserwowanego elementu, bez względu na przyczynę zmiany.
- Element.getBoundingClientRect()Zwraca rozmiar elementu i jego położenie względem widocznego obszaru okna w postaci obiektu DOMRect.
Widzisz błąd albo brakuje przykładu? Napisz do nas.