onComplete

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

В Motion One завершением анимации считается достижение конечного ключевого кадра с учётом всех параметров: длительности, задержек, повторов и easing-функций. Callback onComplete вызывается строго после того, как:

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

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

Сигнатура и место в API

В Motion One onComplete передаётся как часть объекта опций при создании анимации:

animate(
  element,
  { opacity: 1, transform: "translateY(0px)" },
  {
    duration: 0.6,
    easing: "ease-out",
    onComplete: () => {
      // финальная логика
    }
  }
)

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

Жизненный цикл анимации и роль onComplete

Чтобы понять место onComplete, важно рассмотреть упрощённый жизненный цикл:

  1. Инициализация анимации
  2. Применение стартовых значений
  3. Интерполяция свойств по времени
  4. Достижение конечных значений
  5. Вызов onComplete
  6. Освобождение ресурсов анимации

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

Поведение при разных сценариях

Обычное завершение

При стандартном течении анимации callback вызывается единожды:

animate(box, { x: 300 }, {
  duration: 1,
  onComplete: () => {
    console.log("Анимация завершена")
  }
})

В этом случае onComplete срабатывает после прохождения всех кадров.

Повтор анимации (repeat)

При использовании повторов важно учитывать, что onComplete вызывается только после полного завершения всех циклов:

animate(box, { rotate: 360 }, {
  duration: 1,
  repeat: 2,
  onComplete: () => {
    console.log("Все повторы завершены")
  }
})

Если анимация повторяется 2 раза, callback сработает только после третьего финального прохода.

Бесконечный repeat

При repeat: Infinity onComplete не вызывается никогда, так как финального состояния не существует.

Прерывание анимации

При остановке анимации через API (stop, cancel, изменение target-свойств) onComplete не вызывается. Это важное отличие от некоторых других анимационных систем, где существует отдельный callback отмены.

Связь с Promise-цепочками

Motion One поддерживает Promise-совместимое завершение анимаций. onComplete часто используется как аналог .then() в цепочках:

animate(box, { x: 200 }, { duration: 0.5 })
  .finished
  .then(() => {
    console.log("Завершено через Promise")
  })

Однако onComplete выполняется независимо от Promise-механизма и не заменяет его. Различие заключается в том, что:

  • onComplete — синхронный callback
  • .finished — Promise, который резолвится после завершения

Синхронность и порядок выполнения

onComplete выполняется синхронно в момент завершения анимации. Это означает, что:

  • он вызывается до следующего кадра рендеринга,
  • все DOM-изменения уже применены,
  • последующие анимации могут стартовать немедленно.

Пример цепочки:

animate(a, { opacity: 0 }, {
  onComplete: () => {
    animate(b, { opacity: 1 })
  }
})

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

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

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

  • скрытие элементов после fade-out
  • переключение экранов
  • фиксация финального состояния drag-анимаций
  • обновление данных после переходов

Пример:

animate(modal, { opacity: 0, scale: 0.9 }, {
  duration: 0.3,
  onComplete: () => {
    modal.style.display = "none"
  }
})

Здесь важно, что DOM-стили изменяются только после завершения визуального перехода, что предотвращает резкие скачки интерфейса.

Взаимодействие с keyframes

При использовании ключевых кадров onComplete вызывается только после завершения всей последовательности:

animate(element, {
  x: [0, 100, 50, 0]
}, {
  duration: 2,
  onComplete: () => {
    console.log("Все keyframes завершены")
  }
})

Callback не вызывается между промежуточными ключевыми точками, даже если они визуально выглядят как завершённые этапы.

Особенности при работе с transform и layout

При анимации свойств, влияющих на layout (например, height, width, top), onComplete гарантирует, что:

  • все вычисления layout завершены,
  • браузер применил итоговые стили,
  • нет ожидающих reflow операций, связанных с данной анимацией.

Это делает callback надёжной точкой для чтения DOM-метрик:

animate(box, { height: 300 }, {
  onComplete: () => {
    const finalHeight = box.getBoundingClientRect().height
  }
})

Типичные ошибки использования

Запуск логики до завершения анимации

Ошибка возникает при попытке использовать промежуточные состояния вместо onComplete:

animate(box, { x: 200 })
console.log("готово") // неверно

Правильный вариант:

animate(box, { x: 200 }, {
  onComplete: () => {
    console.log("готово")
  }
})

Ожидание вызова при cancel

onComplete не вызывается при отмене анимации, поэтому логика, завязанная на обязательное выполнение callback, может быть нарушена при динамическом управлении UI.

Использование для частых обновлений

onComplete не предназначен для отслеживания прогресса. Он не вызывается во время анимации и не подходит для:

  • прогресс-баров
  • синхронизации кадров
  • realtime-обновлений

Для этих задач используется onUpdate.

Сочетание с несколькими анимациями

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

animate(a, { x: 100 }, { onComplete: () => console.log("A done") })
animate(b, { y: 100 }, { onComplete: () => console.log("B done") })

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

Композиция анимаций через onComplete

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

animate(a, { x: 100 }, {
  onComplete: () => {
    animate(a, { y: 100 }, {
      onComplete: () => {
        animate(a, { scale: 1.2 })
      }
    })
  }
})

Такая структура формирует цепочку, где каждый шаг начинается строго после завершения предыдущего.

Производительность и ограничения

onComplete не создаёт дополнительной нагрузки на каждый кадр, так как вызывается единожды. Однако при массовом создании анимаций важно учитывать:

  • большое количество callback-ов может усложнить управление состоянием,
  • глубокая вложенность onComplete снижает читаемость,
  • предпочтительнее комбинировать с Promise API при сложной логике.

Взаимодействие с серверным рендерингом

В SSR-сценариях onComplete не выполняется на сервере, так как отсутствует жизненный цикл анимации. Callback активируется только в браузерной среде после гидратации и старта анимации.

Паттерны использования

Финальная фиксация состояния

animate(card, { rotateY: 180 }, {
  onComplete: () => {
    card.classList.add("flipped")
  }
})

Запуск следующей сцены

animate(scene1, { opacity: 0 }, {
  onComplete: () => animate(scene2, { opacity: 1 })
})

Очистка временных эффектов

animate(button, { scale: 1.1 }, {
  onComplete: () => {
    button.style.transform = ""
  }
})

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

При повторном запуске анимации на том же элементе предыдущий onComplete может быть проигнорирован, если старая анимация была заменена новой. Это связано с тем, что Motion One привязывает callback к конкретному экземпляру анимации, а не к DOM-элементу.

Контекст использования в архитектуре интерфейсов

onComplete выступает как точка синхронизации между визуальным слоем и логикой приложения. Его использование формирует чёткое разделение:

  • анимация управляет визуальным переходом
  • onComplete фиксирует переход в состоянии приложения
  • бизнес-логика реагирует на завершение, а не на процесс