Удаление обработчиков событий

Работа с событиями в Lottie Web строится вокруг объекта AnimationItem, который возвращается после создания анимации через lottie.loadAnimation. Этот объект содержит механизм подписки и отписки от событий, а также методы жизненного цикла, влияющие на очистку памяти и прекращение всех внутренних обработчиков.

Корректное удаление обработчиков событий критично при работе с динамически создаваемыми анимациями, SPA-интерфейсами и повторной инициализацией анимаций в одном DOM-контейнере.


Архитектура событий AnimationItem

Внутри Lottie Web используется событийная модель, где каждый экземпляр анимации хранит собственный список подписчиков. Подписка выполняется через:

  • addEventListener(eventName, callback)
  • removeEventListener(eventName, callback)

Каждое событие регистрируется в локальном реестре объекта анимации, а не глобально. Это означает, что утечки памяти возникают только при сохранении ссылок на callback или при отсутствии явной очистки экземпляра.


Основные типы событий

В Lottie Web используется набор стандартных событий жизненного цикла:

  • enterFrame — срабатывает при каждом кадре анимации
  • loopComplete — завершение цикла воспроизведения
  • complete — завершение всей анимации (если loop = false)
  • segmentStart — начало сегмента
  • data_ready — данные анимации загружены
  • DOMLoaded — DOM-анимация готова к отображению
  • destroy — экземпляр уничтожается

Каждое из этих событий может иметь зарегистрированные обработчики, которые требуется удалять вручную при динамическом управлении анимациями.


Удаление конкретного обработчика

Удаление происходит через передачу идентичной ссылки на функцию:

function onComplete() {
  console.log('Animation completed');
}

animation.addEventListener('complete', onComplete);

// удаление обработчика
animation.removeEventListener('complete', onComplete);

Ключевой момент — ссылка на функцию должна совпадать. Анонимные функции невозможно удалить:

// Невозможно удалить
animation.addEventListener('complete', () => {
  console.log('done');
});

Очистка всех обработчиков через destroy

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

animation.destroy();

Метод выполняет:

  • удаление всех event listener’ов
  • остановку анимации
  • очистку внутренних таймеров
  • освобождение DOM-ссылок внутри контейнера

После вызова destroy объект становится непригодным для дальнейшего использования.


Сценарии утечек при отсутствии удаления

При повторной инициализации анимации без очистки возникают типичные проблемы:

  • дублирование обработчиков enterFrame
  • многократные вызовы callback на одно событие
  • рост потребления памяти при частой смене экранов
  • сохранение ссылок на DOM-контейнеры

Особенно это проявляется при повторных вызовах lottie.loadAnimation в одном и том же контейнере без предварительного destroy.


Управление жизненным циклом экземпляра

При работе с динамическими интерфейсами важен полный цикл:

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

Пример корректной схемы:

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

function onFrame(e) {
  console.log(e.currentTime);
}

animation.addEventListener('enterFrame', onFrame);

// очистка перед пересозданием
animation.removeEventListener('enterFrame', onFrame);
animation.destroy();
animation = null;

Особенности удаления в циклических анимациях

При loop: true события complete могут не срабатывать, поэтому управление логикой завершения часто переносится на:

  • loopComplete
  • enterFrame с проверкой состояния
  • внешнее управление таймингом

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


Повторная инициализация в одном контейнере

Lottie Web не выполняет автоматическую очистку DOM-контейнера при повторном loadAnimation. Это приводит к накоплению SVG/Canvas слоёв.

Типовой корректный паттерн:

if (animation) {
  animation.destroy();
}

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

Влияние removeEventListener на производительность

Частое добавление и удаление обработчиков без необходимости может приводить к:

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

Оптимальная стратегия — минимизация динамических подписок и группировка логики в один обработчик.


Контекст использования в SPA

В одностраничных приложениях (React, Vue, Angular) жизненный цикл компонентов напрямую влияет на корректность удаления обработчиков.

Ключевой принцип:

  • при монтировании — создание animation instance
  • при размонтировании — вызов destroy

Отсутствие очистки приводит к накоплению скрытых анимаций, даже если DOM-узел уже удалён.


Состояние объекта после удаления

После выполнения:

animation.destroy();

объект:

  • перестаёт генерировать события
  • теряет связь с DOM
  • не реагирует на методы управления
  • может содержать null внутренние поля

Попытка повторного использования требует создания нового экземпляра через lottie.loadAnimation.