onUpdate

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

Внутренний цикл анимации в Motion One строится вокруг высокочастотного обновления состояния. На каждом кадре вычисляются:

  • текущее время анимации
  • прогресс (progress)
  • интерполированные значения свойств
  • применённые easing-функции

onUpdate подключается после вычисления этих данных, но до финального применения изменений к DOM, что делает его пригодным для наблюдения и побочных эффектов.

Базовая форма использования onUpdate

Колбэк onUpdate передаётся в конфигурацию анимации через animate:

import { animate } from "motion";

animate(
  ".box",
  { x: 300 },
  {
    onUpdate: (latest) => {
      console.log(latest.x);
    }
  }
);

В данном контексте latest содержит текущее состояние всех анимируемых свойств. Значения уже интерполированы с учётом easing и времени.

Структура объекта latest

latest, поступающий в onUpdate, включает:

  • текущие значения свойств (x, y, scale, opacity и т.д.)
  • нормализованный прогресс анимации
  • дополнительные вычисленные параметры при наличии timeline

Пример структуры:

{
  x: 120.5,
  y: 0,
  opacity: 0.7,
  scale: 1.1
}

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

Использование прогресса анимации

Помимо значений свойств, onUpdate часто применяется для контроля прогресса:

animate(
  ".box",
  { x: 500 },
  {
    onUpdate: (latest) => {
      const progress = latest.progress;
      console.log(progress);
    }
  }
);

progress нормализован в диапазоне от 0 до 1 и отражает состояние выполнения анимации независимо от длительности или easing.

Синхронизация внешних эффектов

onUpdate используется для синхронизации анимации с внешними системами: canvas-рендерингом, аудио-параметрами или кастомными визуальными эффектами.

animate(
  ".box",
  { rotate: 360 },
  {
    onUpdate: ({ rotate }) => {
      drawSpinner(rotate);
    }
  }
);

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

Поток данных внутри onUpdate

Внутренний поток данных в Motion One проходит несколько стадий:

  1. расчёт времени кадра
  2. применение easing-функции
  3. интерполяция значений
  4. формирование объекта состояния
  5. вызов onUpdate
  6. применение DOM-изменений

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

Отличие onUpdate от onComplete

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

animate(
  ".box",
  { x: 400 },
  {
    onUpdate: ({ x }) => {
      console.log("frame:", x);
    },
    onComplete: () => {
      console.log("finished");
    }
  }
);

Разделение этих двух механизмов позволяет отделять потоковые вычисления от финальных действий.

Производительность onUpdate

Частота вызова onUpdate соответствует частоте кадров браузера, обычно 60 FPS. Это накладывает ограничения на выполняемые внутри него операции.

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

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

Оптимальная модель — использование onUpdate как источника данных, а не места для сложной логики.

Использование с несколькими свойствами

При анимации нескольких свойств onUpdate получает агрегированный объект:

animate(
  ".box",
  {
    x: 300,
    y: 150,
    opacity: 0.5
  },
  {
    onUpdate: (latest) => {
      const { x, y, opacity } = latest;
      render(x, y, opacity);
    }
  }
);

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

Связь с временной шкалой (timeline)

В timeline-режиме Motion One onUpdate получает значения, зависящие от общей временной координаты всех анимаций.

import { timeline } from "motion";

timeline([
  [".box", { x: 200 }, { duration: 1 }],
  [".box", { y: 100 }, { duration: 1 }]
], {
  onUpdate: (latest) => {
    console.log(latest);
  }
});

В этом случае объект состояния отражает комбинированный результат нескольких анимационных шагов.

Использование для построения реактивных систем

onUpdate часто применяется как мост между императивной анимацией и реактивной архитектурой:

  • обновление store (Redux, Zustand)
  • синхронизация с canvas/WebGL
  • управление звуковыми параметрами
  • реализация кастомных easing-сценариев
animate(".box", { x: 1000 }, {
  onUpdate: (latest) => {
    store.setState({ position: latest.x });
  }
});

Пограничные случаи поведения

В некоторых сценариях onUpdate может вызываться:

  • при изменении скорости playback
  • при паузе и возобновлении анимации
  • при динамическом изменении целей анимации

Motion One гарантирует согласованность состояния, поэтому даже при прерывании анимации последний latest остаётся доступным в рамках текущего кадра.

Композиция нескольких onUpdate

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

  • локальный onUpdate конкретной анимации
  • глобальные эффекты timeline
  • внешние слушатели состояния

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