Синхронизация requestAnimationFrame

requestAnimationFrame — это встроенный механизм браузера для выполнения анимаций, синхронизированных с частотой обновления экрана. В отличие от setTimeout и setInterval, он учитывает производительность системы и приостанавливается, если вкладка неактивна.

Ключевые особенности:

  • вызов функции перед каждой перерисовкой кадра
  • автоматическая оптимизация частоты (обычно ~60 FPS)
  • снижение нагрузки на CPU и батарею
  • синхронизация с рендерингом браузера

Пример базового использования:

function animate(time) {
  // time — время с начала работы requestAnimationFrame
  requestAnimationFrame(animate);
}

requestAnimationFrame(animate);

Роль requestAnimationFrame в anime.js

Библиотека anime.js полностью опирается на requestAnimationFrame для управления анимациями. Это означает:

  • единый цикл обновления для всех анимаций
  • отсутствие рассинхронизации между элементами
  • высокая производительность даже при большом количестве анимаций

Внутри anime.js создаётся собственный цикл рендеринга, который:

  1. запускается при наличии активных анимаций
  2. обновляет значения свойств
  3. применяет их к DOM
  4. останавливается, если анимации завершены

Внутренний цикл анимации

anime.js реализует централизованный движок, который можно условно представить следующим образом:

let activeAnimations = [];

function engine(time) {
  for (let i = 0; i < activeAnimations.length; i++) {
    activeAnimations[i].tick(time);
  }

  if (activeAnimations.length > 0) {
    requestAnimationFrame(engine);
  }
}

Каждая анимация регистрируется в общем массиве и обновляется на каждом кадре.


Синхронизация нескольких анимаций

Использование одного requestAnimationFrame-цикла позволяет:

  • гарантировать одинаковый тайминг для всех анимаций
  • избежать дрожания (jank)
  • синхронизировать сложные последовательности

Пример:

anime({
  targets: '.box',
  translateX: 250,
  duration: 2000
});

anime({
  targets: '.circle',
  translateY: 150,
  duration: 2000
});

Обе анимации выполняются в одном цикле и обновляются одновременно.


Тайминг и вычисление прогресса

anime.js использует время, переданное в requestAnimationFrame, для вычисления прогресса анимации:

progress = (currentTime - startTime) / duration;

Это обеспечивает:

  • независимость от FPS
  • плавность при просадках производительности
  • корректное завершение анимации

Управление кадрами и FPS

Хотя requestAnimationFrame стремится к ~60 FPS, фактическое значение зависит от:

  • мощности устройства
  • загруженности браузера
  • сложности DOM

anime.js компенсирует это за счёт:

  • интерполяции значений
  • использования реального времени вместо количества кадров

Пауза и возобновление

При переключении вкладки браузер автоматически приостанавливает requestAnimationFrame. anime.js корректно обрабатывает это:

  • время не “ускоряется” после возврата
  • анимация продолжается с правильного момента

Пример логики:

if (document.hidden) {
  // приостановка обновлений
}

Оптимизация производительности

Синхронизация через requestAnimationFrame позволяет:

Минимизировать перерисовки:

  • все изменения применяются за один кадр

Избегать layout thrashing:

  • чтение и запись DOM разделены

Использовать GPU-ускорение:

  • трансформации (transform, opacity) обновляются эффективно

Использование собственного цикла

anime.js позволяет интегрироваться с внешними циклами:

anime.running.forEach(anim => anim.pause());

function customLoop(time) {
  anime.running.forEach(anim => anim.tick(time));
  requestAnimationFrame(customLoop);
}

requestAnimationFrame(customLoop);

Это полезно при:

  • разработке игр
  • интеграции с Canvas/WebGL
  • синхронизации с другими движками

Связь с таймлайнами

Таймлайны в anime.js также используют общий requestAnimationFrame:

let timeline = anime.timeline();

timeline
  .add({
    targets: '.box',
    translateX: 200
  })
  .add({
    targets: '.box',
    translateY: 200
  });

Каждый шаг таймлайна рассчитывается в рамках одного цикла, что гарантирует:

  • точные задержки
  • предсказуемое поведение
  • отсутствие накопления ошибок

Управление глобальным временем

anime.js поддерживает глобальный контроль времени:

anime.speed = 0.5;

Это влияет на все анимации, так как:

  • используется общий временной поток
  • пересчёт происходит внутри одного requestAnimationFrame

Обработка пропущенных кадров

Если кадры пропускаются (например, из-за нагрузки), anime.js:

  • не пытается “догнать” каждый кадр
  • вычисляет актуальное состояние напрямую

Это предотвращает:

  • скачки анимации
  • накопление задержек
  • визуальные артефакты

Сравнение с setTimeout / setInterval

Подход Особенности
setInterval фиксированный интервал, не синхронизирован с рендерингом
setTimeout нет гарантии точного времени
requestAnimationFrame синхронизация с экраном, оптимизация браузером

anime.js использует исключительно requestAnimationFrame, избегая недостатков альтернатив.


Практические ограничения

Несмотря на преимущества, существуют ограничения:

  • зависимость от частоты обновления дисплея
  • возможные просадки при сложных сценах
  • влияние сторонних скриптов

Глубокая интеграция с браузером

requestAnimationFrame работает на уровне браузерного рендеринга:

  1. JavaScript выполняется
  2. вычисляется layout
  3. происходит paint
  4. вызывается requestAnimationFrame

anime.js вставляется в этот цикл перед перерисовкой, что обеспечивает:

  • минимальную задержку
  • максимальную плавность
  • синхронность всех визуальных изменений