Вебсправочник
Основы веб-разработки

requestAnimationFrame: движение по времени кадра

RequestAnimationFrame вызывает функцию перед подходящей отрисовкой браузера. Для движения нужно учитывать прошедшее время, а не добавлять одинаковое расстояние на каждом кадре.

Назначение

RequestAnimationFrame вызывает функцию перед подходящей отрисовкой браузера. Для движения нужно учитывать прошедшее время, а не добавлять одинаковое расстояние на каждом кадре.

Как это работает

В собственной странице создайте небольшой блок id frame-demo с запасом места справа. Пример двигает его на 60 пикселей за 600 миллисекунд. Предпочтение уменьшенного движения проверяется перед запуском.

Пример

const element = document.querySelector("#frame-demo");
let frame = null;
let start;
function draw(time) {
  if (start === undefined) start = time;
  const progress = Math.min((time - start) / 600, 1);
  element.style.transform = `translateX(${60 * progress}px)`;
  frame = progress < 1 ? requestAnimationFrame(draw) : null;
}
if (!matchMedia("(prefers-reduced-motion: reduce)").matches) {
  frame = requestAnimationFrame(draw);
}
function stopDemo() {
  if (frame !== null) cancelAnimationFrame(frame);
  frame = null;
}

Что должно получиться

Положение зависит от временной метки callback, поэтому частота экрана не задаёт скорость напрямую. При reduced-motion начальный запуск пропускается. StopDemo отменяет следующий ожидающий кадр.

Граница применения и частая ошибка

В фоновой вкладке кадры обычно приостанавливаются; это не таймер для обязательной серверной работы. Код проверяет настройку движения только в начале — рабочий компонент должен учитывать её изменение. Для простой CSS-анимации JavaScript может быть избыточным.

Самостоятельная проверка

Сравните обычный режим и уменьшенное движение. Остановите пример до завершения и проверьте, что кадры не продолжаются. Затем переключите вкладку во время анимации и объясните возможный скачок прогресса по времени.

Самостоятельный учебный материал. Техническая документация: первоисточник по теме ↗. Если результат отличается, проверьте код и версию браузера.