Контроль воспроизведения

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


Любая анимация в Popmotion (tween, spring, keyframes, physics) возвращает объект управления. Этот объект представляет собой интерфейс между описанием движения и его фактическим выполнением.

Ключевая идея:

анимация ≠ процесс воспроизведения

Анимация описывает поведение, а контроллер управляет его жизненным циклом.

Типичный контроллер предоставляет методы:

  • stop() — полная остановка
  • pause() — временная пауза
  • play() — продолжение
  • seek() — переход к конкретному моменту времени
  • complete() — принудительное завершение
  • timeScale — масштаб времени (ускорение/замедление)

Запуск и остановка воспроизведения

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

import { animate } from "popmotion";

const animation = animate({
  from: 0,
  to: 100,
  duration: 2000,
  onUpdate: (v) => console.log(v)
});

Остановка выполняет полное прекращение работы:

animation.stop();

После вызова stop() анимация не может быть продолжена в прежнем состоянии. Это важно отличать от паузы.


Пауза и продолжение воспроизведения

Пауза сохраняет текущее состояние прогресса, не обнуляя внутреннее время.

animation.pause();

Возобновление:

animation.play();

Поведение при паузе:

  • сохраняется текущий прогресс
  • внутренний таймер замораживается
  • onUpdate перестаёт вызываться
  • при play() продолжение идёт с точки остановки

Это делает Popmotion удобным для UI-интерактивных сценариев: модальные окна, скрытие вкладок, переключение сцен.


Различие между stop и pause

Критическое различие:

Метод Состояние прогресса Возможность продолжения
pause сохраняется да
stop завершён/сброшен нет

stop() используется, когда результат анимации больше не нужен. pause() — когда важно сохранить контекст.


Управление временем и масштабирование скорости

Popmotion позволяет управлять скоростью воспроизведения через масштаб времени.

animation.timeScale = 2;

Поведение:

  • 1 — нормальная скорость
  • 2 — в 2 раза быстрее
  • 0.5 — в 2 раза медленнее
  • 0 — фактическая пауза

Это особенно полезно при создании эффектов:

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

Перемотка и установка прогресса

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

animation.seek(0.5);

Где значение:

  • 0 — начало
  • 1 — конец
  • промежуточные значения — доля прогресса

Поведение seek():

  • мгновенно изменяет состояние
  • не вызывает «плавного перехода»
  • может использоваться даже в paused состоянии

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

slider.onin put = (e) => {
  animation.seek(e.target.value / 100);
};

Работа с прогрессом и временем

Внутреннее состояние анимации опирается на:

  • elapsed time (прошедшее время)
  • duration
  • easing function
  • current progress (0–1)

Popmotion вычисляет значение по формуле:

  • нормализация времени
  • применение easing
  • интерполяция между from и to

Хотя формально это скрыто, контроль через seek() позволяет вмешиваться в этот процесс напрямую.


Прерывание и переключение анимаций

В интерфейсных сценариях часто требуется заменить одну анимацию другой без резких скачков.

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

let currentAnimation;

function playNewAnimation() {
  if (currentAnimation) currentAnimation.stop();

  currentAnimation = animate({
    from: 0,
    to: 300,
    duration: 1000,
    onUpdate: (v) => console.log(v)
  });
}

Это предотвращает:

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

Управление состоянием через события

Popmotion предоставляет обратные вызовы, которые тесно связаны с контролем воспроизведения:

  • onUpdate — каждое изменение значения
  • onComplete — завершение
  • onPlay — запуск
  • onStop — остановка
  • onPause — пауза

Пример синхронизации состояния UI:

const animation = animate({
  from: 0,
  to: 1,
  onUpdate: (v) => {
    element.style.opacity = v;
  },
  onComplete: () => {
    console.log("готово");
  }
});

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

Контроль воспроизведения часто связывается с событиями DOM:

buttonPlay.oncl ick = () => animation.play();
buttonPause.oncl ick = () => animation.pause();
buttonStop.oncl ick = () => animation.stop();

Более сложный сценарий — drag-to-control:

element.onmousem ove = (e) => {
  const progress = e.clientX / window.innerWidth;
  animation.seek(progress);
};

Такой подход превращает анимацию в управляемый параметр интерфейса.


Поведение при изменении состояния во время выполнения

При одновременных действиях важно учитывать приоритет:

  • stop() всегда завершает цикл немедленно
  • pause() блокирует тик обновления
  • seek() может выполняться как в paused, так и running состоянии
  • изменение timeScale применяется мгновенно

Конфликты могут возникать при частом вызове seek() и play() одновременно, поэтому обычно вводится логика блокировки:

let isScrubbing = false;

slider.onmoused own = () => isScrubbing = true;
slider.onmous eup = () => isScrubbing = false;

slider.onin put = (e) => {
  if (isScrubbing) {
    animation.seek(e.target.value / 100);
  }
};

Комбинирование контроля с timeline

При использовании timeline управление становится иерархическим.

import { timeline } from "popmotion";

const sequence = timeline([
  { track: "x", from: 0, to: 100, duration: 500 },
  { track: "x", from: 100, to: 200, duration: 500 }
]);

Контроль работает на уровне всей последовательности:

  • пауза останавливает весь таймлайн
  • seek перемещает позицию внутри цепочки
  • stop завершает всю последовательность

Это делает timeline основным инструментом для сложных интерфейсных переходов.


Состояния жизненного цикла анимации

Контроллер обычно проходит через состояния:

  • idle (не запущен)
  • running (выполняется)
  • paused (приостановлен)
  • stopped (завершён)

Переходы между ними определяют, какие методы доступны в данный момент. Например:

  • после stop() повторный play() может требовать пересоздания
  • после pause() допустим play()
  • после seek() состояние не меняется автоматически

Управление несколькими анимациями одновременно

При сложных интерфейсах часто требуется синхронный контроль:

const a1 = animate({ from: 0, to: 100 });
const a2 = animate({ from: 100, to: 200 });

function pauseAll() {
  a1.pause();
  a2.pause();
}

Для централизованного управления вводится абстракция контроллера группы:

  • единый play/pause
  • синхронный seek
  • согласованное завершение

Практика построения предсказуемого контроля

Наиболее стабильные системы управления Popmotion-анимациями строятся по принципам:

  • одна анимация — один контролируемый источник
  • запрет параллельного изменения одного свойства
  • явное управление жизненным циклом
  • разделение создания и запуска
  • использование seek() для интерактивности вместо пересоздания

Такой подход обеспечивает детерминированное поведение даже при высокой частоте событий и сложной UI-логике