onComplete

Событие onComplete — один из ключевых механизмов управления жизненным циклом анимации в библиотеке mo.js. Оно срабатывает в момент полного завершения проигрывания анимации, позволяя выполнять дополнительные действия: запуск цепочек, изменение состояния интерфейса, очистку ресурсов или повторную инициализацию.


Общая концепция

В основе mo.js лежит декларативный подход к созданию анимаций. Каждый объект (например, mojs.Tween, mojs.Shape, mojs.Timeline) имеет набор хуков жизненного цикла:

  • onStart — начало анимации
  • onUpdate — каждый кадр
  • onComplete — завершение анимации

onComplete вызывается один раз, когда анимация полностью доходит до конца с учётом всех параметров (duration, delay, repeat и easing).


Базовый синтаксис

const tween = new mojs.Tween({
  duration: 1000,
  onComplete: function () {
    console.log('Анимация завершена');
  }
});

tween.play();

Допускается использование стрелочных функций:

onComplete: () => {
  console.log('Done');
}

Контекст выполнения

По умолчанию внутри onComplete:

  • this указывает на экземпляр анимации (например, Tween)
  • доступны внутренние методы и свойства объекта
onComplete: function () {
  console.log(this.progress); // всегда 1
}

Особенности работы

1. Срабатывание после всех повторений

Если используется repeat, onComplete вызывается после последнего цикла:

new mojs.Tween({
  duration: 500,
  repeat: 3,
  onComplete: () => {
    console.log('Все повторы завершены');
  }
});

Здесь анимация выполнится 4 раза (1 + 3 повтора), и только затем сработает onComplete.


2. Учет задержки (delay)

Задержка влияет только на момент старта, но не на момент вызова onComplete.

new mojs.Tween({
  delay: 1000,
  duration: 500,
  onComplete: () => {
    console.log('Фактическое завершение через 1500 мс');
  }
});

3. Взаимодействие с yoyo

При использовании yoyo: true анимация проигрывается вперёд и назад. onComplete срабатывает после полного цикла вперёд-назад.

new mojs.Tween({
  duration: 500,
  yoyo: true,
  onComplete: () => {
    console.log('Цикл вперёд-назад завершён');
  }
});

Использование в mojs.Shape

const circle = new mojs.Shape({
  shape: 'circle',
  radius: { 0: 50 },
  duration: 1000,
  onComplete: function () {
    this.el.style.opacity = 0;
  }
});

circle.play();

Частый сценарий — удаление DOM-элемента:

onComplete: function () {
  this.el.remove();
}

Использование в mojs.Timeline

Timeline агрегирует несколько анимаций. onComplete срабатывает, когда завершены все вложенные элементы.

const timeline = new mojs.Timeline({
  onComplete: () => {
    console.log('Вся последовательность завершена');
  }
});

timeline.add(tween1, tween2).play();

Цепочки анимаций

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

const first = new mojs.Tween({
  duration: 500,
  onComplete: () => second.play()
});

const second = new mojs.Tween({
  duration: 500
});

first.play();

Асинхронные операции

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

onComplete: async () => {
  await fetch('/api/data');
  console.log('Данные загружены после анимации');
}

Повторный запуск анимации

При повторном вызове .play() onComplete будет вызываться снова:

tween.play();
tween.replay();

Каждый цикл завершения инициирует новый вызов обработчика.


Отмена анимации и onComplete

Если анимация остановлена вручную:

tween.stop();

onComplete не вызывается, так как анимация не достигла конца.


Отличие от onUpdate и onStart

Хук Когда вызывается
onStart В начале анимации
onUpdate Каждый кадр
onComplete После полного завершения

Частые сценарии применения

1. Управление состоянием интерфейса

onComplete: () => {
  button.disabled = false;
}

2. Запуск следующей логики

onComplete: () => {
  showModal();
}

3. Очистка ресурсов

onComplete: function () {
  this.el = null;
}

4. Синхронизация с другими системами

onComplete: () => {
  analytics.track('animation_finished');
}

Ошибки и подводные камни

Потеря контекста this

При использовании стрелочной функции this не указывает на экземпляр:

onComplete: () => {
  console.log(this); // не mojs-объект
}

Решение — использовать обычную функцию:

onComplete: function () {
  console.log(this);
}

Множественные вызовы при неправильной логике

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


Зависимость от duration

Если duration: 0, onComplete вызывается практически мгновенно, что может приводить к неожиданному порядку выполнения.


Комбинирование с промисами

Обёртка для превращения анимации в Promise:

function playTween(tween) {
  return new Promise(resolve => {
    tween.options.onCompl ete = resolve;
    tween.play();
  });
}

await playTween(tween);

Позволяет писать последовательные анимации в стиле async/await.


Внутренний механизм

Внутри mo.js:

  • анимация отслеживает прогресс (progress от 0 до 1)
  • при достижении progress === 1
  • и отсутствии активных повторов
  • вызывается onComplete

Это означает, что событие строго связано с конечным состоянием интерполяции.


Рекомендации по использованию

  • Использовать onComplete для логики завершения, а не промежуточных действий
  • Избегать тяжёлых вычислений внутри обработчика
  • При сложных сценариях использовать Timeline вместо ручных цепочек
  • Контролировать повторные вызовы при replay()

Практический пример

const burst = new mojs.Burst({
  radius: { 0: 100 },
  count: 10,
  duration: 800,
  onComplete: function () {
    console.log('Эффект завершён');
    this.el.remove();
  }
});

burst.play();

В данном случае:

  • создаётся burst-анимация
  • после её завершения элемент удаляется из DOM
  • выполняется пользовательская логика

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

onComplete играет роль точки синхронизации:

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

В сложных интерфейсах он выступает аналогом событийного обработчика в системах управления состоянием.