Событие enterFrame

Событие enterFrame является одним из ключевых механизмов синхронизации логики с проигрыванием анимации в Lottie Web. Оно вызывается на каждом шаге рендеринга кадра и позволяет привязывать произвольное поведение к текущему состоянию анимации.

Внутри движка Lottie анимация работает через цикл обновления, основанный на requestAnimationFrame. На каждом цикле вычисляется текущий кадр, после чего происходит рендеринг композиции и генерация событий. enterFrame эмитится непосредственно после пересчёта состояния текущего кадра.

Основная характеристика события — высокая частота вызова. Оно срабатывает:

  • при каждом отрисованном кадре
  • независимо от типа воспроизведения (play, reverse, pause с ручным обновлением)
  • синхронно с внутренним таймингом анимации

Подписка на событие

Событие регистрируется через стандартный механизм слушателей:

const animation = lottie.loadAnimation({
  container: document.getElementById('anim'),
  renderer: 'svg',
  loop: true,
  autoplay: true,
  path: 'animation.json'
});

animation.addEventListener('enterFrame', function (event) {
  console.log(event.currentFrame);
});

Каждый вызов обработчика содержит объект состояния текущего кадра.

Структура объекта события

Объект, передаваемый в обработчик enterFrame, содержит данные о текущем положении анимации:

  • currentFrame — текущий кадр анимации
  • totalFrames — общее количество кадров композиции
  • direction — направление воспроизведения (1 или -1)
  • time или эквивалентное значение прогресса времени

Эти данные отражают текущее состояние временной шкалы и позволяют синхронизировать внешнюю логику с анимацией.

Принцип работы в цикле воспроизведения

Во время проигрывания анимации происходит последовательность операций:

  1. вычисление времени с момента старта
  2. преобразование времени в номер кадра
  3. обновление состояния композиции
  4. рендеринг слоя
  5. генерация enterFrame

Таким образом, событие находится на границе между обновлением состояния и визуальным выводом.

Использование для синхронизации интерфейса

На уровне интеграции с DOM enterFrame часто используется для привязки интерфейсных элементов к анимации. Типовые сценарии:

  • синхронизация прогресс-бара
  • управление числовыми счётчиками
  • изменение CSS-свойств в зависимости от кадра
  • координация нескольких анимаций

Пример синхронизации прогресса:

animation.addEventListener('enterFrame', function (e) {
  const progress = e.currentFrame / e.totalFrames;
  document.querySelector('.progress').style.width = (progress * 100) + '%';
});

Управление производительностью

Высокая частота вызова делает enterFrame чувствительным к нагрузке. Любая тяжёлая операция внутри обработчика напрямую влияет на плавность анимации.

Типичные ограничения:

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

Оптимизация обычно строится на переносе вычислений в отдельные структуры состояния:

let cachedValue = 0;

animation.addEventListener('enterFrame', function (e) {
  cachedValue = e.currentFrame * 2;
});

Направление воспроизведения

Поле direction позволяет учитывать режим проигрывания анимации:

  • 1 — прямое воспроизведение
  • -1 — обратное воспроизведение

Это особенно важно при реализации интерактивных сценариев, где анимация может перемещаться в обе стороны.

animation.addEventListener('enterFrame', function (e) {
  if (e.direction === -1) {
    console.log('обратное воспроизведение');
  }
});

Отличие от событий завершения и сегментов

В отличие от событий вроде complete или loopComplete, enterFrame не сигнализирует о переходах состояния. Оно отражает непрерывный процесс движения временной шкалы.

Ключевые отличия:

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

Применение в интерактивных сценариях

При управлении анимацией через внешний ввод (скролл, drag, pointer movement) enterFrame используется как точка привязки для пересчёта состояния:

  • скролл → текущий кадр
  • жест → позиция на timeline
  • аудио → временная шкала

Пример привязки к скроллу:

window.addEventListener('scroll', function () {
  const scrollTop = window.scrollY;
  const maxScroll = document.body.scrollHeight - window.innerHeight;

  const frame = (scrollTop / maxScroll) * animation.totalFrames;
  animation.goToAndStop(frame, true);
});

Даже при использовании внешнего управления событие enterFrame продолжает отражать текущее состояние после каждого обновления кадра.

Особенности работы в SVG и Canvas рендерерах

Поведение события не зависит от выбранного рендерера, однако нагрузка различается:

  • SVG: большее влияние DOM-операций внутри enterFrame
  • Canvas: выше общая производительность, но дороже перерисовка сцены
  • HTML: зависит от количества управляемых элементов

С точки зрения API событие остаётся идентичным, различается только стоимость его частого вызова.

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

При работе с несколькими экземплярами Lottie синхронизация часто строится через общий обработчик:

function sync(e) {
  animation2.goToAndStop(e.currentFrame, true);
}

animation1.addEventListener('enterFrame', sync);

Такой подход позволяет выстраивать композиции из независимых анимаций с единым временным источником.

Ограничения использования

Из-за высокой частоты вызова enterFrame становится критическим участком производительности. Основные ограничения:

  • недопустимы сетевые запросы
  • нежелательны перерасчёты сложных геометрий
  • неэффективны частые обращения к layout

При некорректном использовании возникает деградация плавности кадров и увеличение времени рендеринга каждого цикла анимации.