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.
Na tej stronie

Przykład

#
JavaScript
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')
Konsola
Animacja zaplanowana
Pasek wypełniony: 100%

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

#
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 przez requestAnimationFrame(), przekazywany do cancelAnimationFrame().

Więcej przykładów

#
Licznik punktów krok po kroku
JavaScript
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)
Konsola
30
60
90
120
150

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.

Anulowanie zaplanowanej klatki
JavaScript
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)
Konsola
Identyfikator klatki to number
Klatka gotowa, przesunięcie: brak

Pierwsza zaplanowana funkcja została anulowana przed najbliższą klatką, więc kafelek się nie przesunął. Druga wykonała się normalnie.

Najwyżej jedna aktualizacja na klatkę
JavaScript
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 transition albo @keyframes. Przeglądarka animuje je wydajniej i bez kodu JavaScript.

Powiązane hasła

#

Widzisz błąd albo brakuje przykładu? Napisz do nas.