Уничтожение анимаций

В системе mo.js любая анимация представляет собой объект с собственным циклом существования: инициализация, запуск, обновление через requestAnimationFrame, завершение или принудительная остановка. При этом сам факт окончания визуального эффекта не гарантирует освобождение ресурсов. Без явного управления жизненным циклом остаются активные таймеры, подписки на события и ссылки на объекты, что приводит к накоплению мусора в памяти и деградации производительности при длительной работе приложения.

Особенно критичными становятся сценарии:

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

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


Базовые механизмы остановки анимаций

В mo.js каждая анимационная сущность (Tween, Shape, Burst, Timeline) предоставляет набор методов управления состоянием выполнения. Основные из них:

stop()

Принудительно останавливает выполнение анимации в текущем кадре. При этом внутренний таймер requestAnimationFrame перестает обновлять состояние объекта.

Используется в ситуациях, когда требуется немедленно прекратить визуальное воспроизведение:

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

После вызова stop() объект остается в памяти и может быть повторно запущен.


pause()

Приостанавливает анимацию без сброса прогресса. В отличие от остановки, текущее состояние сохраняется, и продолжение возможно с того же кадра.

Используется для временной заморозки:

  • скрытие вкладки интерфейса
  • потеря фокуса окна
  • временная блокировка UI

Важно учитывать, что при частом использовании pause без последующего уничтожения сохраняется активная структура объекта.


reset()

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

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

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

Полное уничтожение Tween-объектов

Tween в mo.js является базовым строительным блоком анимации. При создании большого количества tween-ов основная проблема заключается в их накоплении в памяти, особенно если они больше не используются.

Типовой сценарий управления жизненным циклом:

  • остановка выполнения
  • удаление из внутренних коллекций (если используется timeline)
  • разрыв ссылок на объект
const tween = new mojs.Tween({
  duration: 1000,
  onUpdate: () => {}
});

// запуск
tween.play();

// уничтожение логически
tween.stop();
tween.reset();

Ключевой момент заключается в том, что JavaScript-движок не может освободить память, пока существует хотя бы одна ссылка на объект tween. Поэтому после завершения использования требуется обнуление ссылок:

tween = null;

Удаление анимаций из Timeline

Timeline в mo.js управляет группами анимаций и синхронизацией их выполнения. При динамическом добавлении tween-ов важно контролировать их удаление.

Основные сценарии уничтожения:

  • удаление отдельных tween-ов из последовательности
  • остановка всего timeline
  • полное освобождение структуры

При остановке timeline:

timeline.stop();

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

При необходимости полной очистки требуется:

  • остановить timeline
  • удалить ссылки на вложенные tween-ы
  • обнулить сам timeline
timeline.stop();
timeline = null;

В архитектурах с динамическими сценами это особенно важно, поскольку старые timeline могут продолжать существовать в памяти даже после смены UI-состояния.


Уничтожение Shape-анимаций

Shape-анимации в mo.js часто связаны с DOM-элементами или SVG-структурами. Их уничтожение требует учета не только JS-объекта, но и визуального представления.

Типичный жизненный цикл:

  • создание shape
  • привязка к DOM/SVG
  • запуск анимации
  • завершение или остановка
  • удаление визуального элемента

После завершения анимации shape может продолжать существовать, если DOM-узел не удалён.

const shape = new mojs.Shape({
  parent: document.body,
  shape: 'circle',
  radius: 20
});

shape.play();

Для полного уничтожения требуется разорвать связь с DOM:

  • остановить анимацию
  • удалить элемент из DOM
  • обнулить ссылку
shape.stop();

shape.el && shape.el.remove?.();

shape = null;

Burst-эффекты и их очистка

Burst является одним из наиболее ресурсоемких объектов, поскольку генерирует множество дочерних анимаций одновременно. При частом использовании (например, клики, hover, интерактивные эффекты) без очистки возникает накопление неиспользуемых tween-ов.

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

Стратегия уничтожения:

  • остановка burst
  • удаление всех дочерних элементов
  • очистка контейнера
  • удаление ссылок
const burst = new mojs.Burst({
  parent: document.body,
  radius: { 0: 100 },
  count: 10
});

burst.play();

После завершения жизненного цикла:

burst.stop();

// очистка DOM контейнера
if (burst.el) {
  burst.el.innerHTML = '';
  burst.el.remove?.();
}

burst = null;

Разрыв событий и подписок

mo.js активно использует callbacks: onStart, onUpdate, onComplete. При неправильном управлении ими могут возникать скрытые утечки памяти, поскольку замыкания удерживают ссылки на внешние переменные.

Проблемные сценарии:

  • сохранение ссылки на DOM внутри onUpdate
  • использование внешних массивов в замыканиях
  • привязка обработчиков без последующего удаления

Рекомендуемая стратегия:

  • минимизация захвата внешнего контекста
  • явное обнуление ссылок после завершения анимации
  • отказ от длительных замыканий в динамических анимациях
const data = { value: 1 };

const tween = new mojs.Tween({
  duration: 1000,
  onUpdate: () => {
    data.value++;
  },
  onComplete: () => {
    // разрыв логических ссылок
  }
});

После завершения:

data = null;

Управление requestAnimationFrame и скрытые циклы

Внутри mo.js используется requestAnimationFrame, который продолжает вызывать обновления до тех пор, пока анимация не остановлена. Основная проблема возникает при:

  • уничтожении DOM без остановки анимации
  • переключении страниц без cleanup
  • утечках ссылок на tween

Даже если визуально ничего не происходит, RAF-цикл может продолжать работать.

Корректная последовательность уничтожения:

  • stop()
  • удаление DOM (если есть)
  • обнуление ссылки
  • удаление из коллекций/таймлайнов

Массовое уничтожение анимаций

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

Типовая стратегия:

  • регистрация всех tween/shape/burst при создании
  • хранение в массиве или WeakMap
  • массовая очистка при смене состояния приложения
const registry = [];

function addAnimation(anim) {
  registry.push(anim);
}

function clearAnimations() {
  registry.forEach(anim => {
    anim.stop?.();
    anim.reset?.();
    anim.el?.remove?.();
  });

  registry.length = 0;
}

В SPA это используется при:

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

Особенности утечек памяти в mo.js

Основные источники утечек:

  • неостановленные tween-ы
  • сохраненные ссылки на DOM-элементы
  • замыкания в callbacks
  • неочищенные timeline-структуры
  • burst-эффекты без удаления контейнера

Ключевой принцип: наличие визуального завершения анимации не означает завершения её жизненного цикла в памяти.


Структурный подход к уничтожению анимаций

В устойчивых архитектурах применяется единый шаблон:

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

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