Веб-версияОткрыть в Telegram

ПостrequestAnimationFrame() — почему время выполнения js нельзя привязывать к фиксированной частоте кадров!

1 сентября 2026
C
Code Ready | Frontend
requestAnimationFrame() — почему время выполнения js нельзя привязывать к фиксированной частоте кадров! Браузер самостоятельно управляет процессом обновления изображения на экране. JavaScript выполняется через event loop и не синхронизирован напрямую с частотой обновления дисплея. Поэтому анимации, построенные на фиксированных задержках, могут вести себя по-разному на разных устройствах. setTimeout() не является механизмом синхронизации с браузерным rendering pipeline: let x = 0; function animate() { x += 5; element.style.transform = `translateX(${x}px)`; setTimeout(animate, 16); } animate(); Значение 16 миллисекунд не означает один кадр. Это только минимальная задержка перед следующим выполнением callback. На дисплее 60 Hz это примерно соответствует одному кадру (~16.67 ms), но на устройствах с частотой 120 Hz, 144 Hz и выше браузер не будет синхронизировать выполнение JavaScript с каждым обновлением экрана. В результате скорость такой анимации зависит от количества вызовов JavaScript, а не от реально прошедшего времени. Для визуальных обновлений используется requestAnimationFrame(): let x = 0; function animate() { x += 5; element.style.transform = `translateX(${x}px)`; requestAnimationFrame(animate); } requestAnimationFrame(animate); Браузер планирует callback перед следующим обновлением изображения. Это позволяет выполнять изменения визуального состояния внутри стандартного цикла рендеринга. Но сама привязка к кадрам не делает анимацию корректной. Если браузер пропустит несколько кадров из-за нагрузки, изменение состояния будет зависеть от количества вызовов requestAnimationFrame(), а не от реального времени. Для независимости от частоты кадров необходимо использовать timestamp, который передаёт requestAnimationFrame(): let startTime = null; function animate(timestamp) { if (startTime === null) { startTime = timestamp; } const elapsed = timestamp - startTime; const x = elapsed * 0.1; element.style.transform = `translateX(${x}px)`; requestAnimationFrame(animate); } requestAnimationFrame(animate); Теперь положение элемента вычисляется через прошедшее время, а не через количество кадров. Такой подход сохраняет одинаковую скорость анимации независимо от того, работает устройство на 60 Hz, 120 Hz или другой частоте обновления. При этом requestAnimationFrame() не отменяет особенности браузерного rendering pipeline. Например, чтение геометрии элемента сразу после изменения стилей может вызвать forced synchronous layout: element.style.width = '500px'; const height = element.offsetHeight; После изменения стиля браузеру необходимо получить актуальное значение layout. Поэтому он может выполнить синхронный перерасчёт перед возвратом значения. В больших интерфейсах такие операции внутри animation loop становятся причиной лишней нагрузки и снижения производительности. Также необходимо управлять жизненным циклом запущенных анимаций. Если компонент удалён, а callback продолжает создавать новые кадры, приложение продолжает выполнять ненужную работу: let frameId; let running = true; function animate() { if (!running) return; update(); frameId = requestAnimationFrame(animate); } frameId = requestAnimationFrame(animate); // cleanup running = false; cancelAnimationFrame(frameId); Кроме того, requestAnimationFrame() не делает любую анимацию автоматически дешёвой. Для оптимальной работы браузеру желательно избегать постоянных layout и paint операций. Свойства вроде transform и opacity чаще позволяют обновлять слой через compositor без полного перерасчёта страницы. 🔥 requestAnimationFrame() — это не просто более плавная альтернатива таймерам. Это механизм синхронизации JavaScript с браузерным циклом обновления изображения, который позволяет выполнять визуальные изменения в правильный момент, рассчитывать анимации через реальное время, учитывать разные частоты обновления экранов и избегать лишних операций при работе с интерфейсом. 📣 Code Ready | #практика
30 · 1.6K ·

Рядом в ленте

CCode Ready | FrontendДелаем «сквиркл»-углы нативно в CSS! Обычный border-radius строит эллиптическое скругление. Но в интерфейсах часто нужны более плавные, «квадратные» скругления CCode Ready | Frontend👩‍💻 Можно ли стилизовать субтитры к видео с помощью CSS? Псевдоэлемент::cue позволяет стилизовать текст субтитров и других текстовых дорожек, отображаемых внутр
это сообщение
CCode Ready | Frontend👩‍💻 Показываем результат работы программы с помощью <samp>! Тег <samp> используется для оформления вывода данных, которые показываются пользователю в результатеCCode Ready | Frontend👨‍💻 Интересная статья попалась на Хабре: «o2 game engine»! В этой статье: • Познакомитесь с компактным open-source движком для 2D/3D-игр на C++ и JavaScript; •
CCode Ready | FrontendCode Ready | Frontend@code_ready · канал · Технологии
21 666подписчиков1 696средний охват поста
Лента площадки Открыть в Telegram

Открытая публичная лента из поискового индекса ChatCrawler — «Google по публичному Telegram»; обновляется по мере обхода площадки. Время — UTC.

Только публичный контент, официальный API Telegram. О проекте · Вопросы · Чего мы не делаем · Убрать страницу из выдачи · Каталог · Поиск · Как мы считаем