Композиция анимаций

Popmotion построена вокруг идеи функциональной композиции: каждая анимация рассматривается как независимый поток значений во времени, который можно преобразовывать, объединять и комбинировать с другими потоками. Это делает систему не набором отдельных эффектов, а конструктором анимационных графов, где поведение формируется через композиционные примитивы.

В основе лежит концепция value stream — непрерывного потока значений, изменяющихся во времени. Любая анимация в Popmotion может быть представлена как функция, которая принимает входное состояние и возвращает новый поток.

Такой подход позволяет:

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

Композиция здесь не ограничивается последовательным запуском — она работает как математическое объединение функций.


Последовательная композиция (sequence)

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

Принцип можно выразить как:

  • A выполняется
  • затем B
  • затем C

Такой подход применяется для построения сценариев интерфейса, где важно линейное развитие событий: появление элементов, переходы экранов, пошаговые эффекты.

Пример логики:

import { tween, chain } from 'popmotion';

chain(
  tween({ from: 0, to: 100, duration: 300 }),
  tween({ from: 100, to: 200, duration: 300 }),
  tween({ from: 200, to: 300, duration: 300 })
);

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


Параллельная композиция (parallel)

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

Используется, когда:

  • нужно одновременно двигать несколько объектов
  • требуется синхронная трансформация (scale, opacity, position)
  • создаются сложные сцены интерфейса

Логическая модель:

  • A, B и C стартуют одновременно
  • каждый идет по своему пути
  • завершение происходит независимо или синхронизировано
import { tween, parallel } from 'popmotion';

parallel([
  tween({ from: 0, to: 100, duration: 500 }),
  tween({ from: 0, to: 1, duration: 500 }),
  tween({ from: 100, to: 200, duration: 500 })
]);

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


Смешанная композиция потоков

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

Например:

  • сначала выполняется fade-in
  • затем одновременно запускаются движение и масштабирование
  • после этого происходит завершающий переход

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


Stagger-композиция

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

Используется для:

  • списков элементов
  • сеток интерфейса
  • появления групп UI-компонентов

Механика:

  • элементы стартуют с фиксированным интервалом
  • каждый следующий начинает раньше завершения предыдущего
import { stagger, tween } from 'popmotion';

stagger(
  Array.from({ length: 5 }).map(() =>
    tween({ from: 0, to: 1, duration: 400 })
  ),
  100
);

Stagger создает эффект волны, где движение распространяется по группе объектов.


Композиция трансформаций значений

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

Типичный сценарий:

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

Формально:

  • input stream → transform → output stream
import { tween, transform } from 'popmotion';

const progress = tween({ from: 0, to: 1, duration: 1000 });

const x = transform(progress, v => v * 300);
const opacity = transform(progress, v => v);

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


Композиция с физическими моделями

Popmotion поддерживает физические анимации (spring, decay), которые особенно полезны при композиции с tween-потоками.

Комбинации используются для:

  • плавных переходов между состояниями
  • реалистичных интерфейсных взаимодействий
  • завершения анимации «естественным» образом

Типичный паттерн:

  • tween задает начальное движение
  • spring завершает траекторию
  • decay добавляет инерцию

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


Слияние потоков (merge)

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

Используется для:

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

Модель:

  • поток A генерирует значение
  • поток B генерирует значение
  • результат — функция объединения
import { merge, tween, spring } from 'popmotion';

merge([
  tween({ from: 0, to: 100, duration: 500 }),
  spring({ from: 100, to: 0, stiffness: 200 })
]);

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


Композиция с пользовательским вводом

Особое место занимает интеграция pointer-событий. В Popmotion ввод сам по себе является потоком значений, который может быть включен в композицию наравне с анимациями.

Сценарии:

  • drag взаимодействует с spring-возвратом
  • движение курсора влияет на параллельные анимации
  • жесты управляют несколькими потоками одновременно

Композиционная модель:

  • input stream → transform → physics response → output stream

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


Деревья анимаций и композиционные графы

Сложные интерфейсы формируются как деревья потоков:

  • листья: tween, spring, decay
  • узлы: chain, parallel, stagger
  • корень: итоговое состояние UI

Такая структура позволяет:

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

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

  • root parallel

    • chain

      • tween
      • spring
    • stagger

      • tween × n

Каждый узел управляет собственным подграфом.


Декларативная композиция состояний

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

Состояние становится центральной точкой:

  • входное состояние
  • промежуточные трансформации
  • выходное состояние

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


Синхронизация неоднородных потоков

Одна из сложностей композиции — объединение потоков с разной длительностью и природой движения. Решение заключается в нормализации:

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

Это позволяет объединять:

  • spring (бесконечное затухание)
  • tween (фиксированная длительность)
  • decay (экспоненциальное затухание)

в одну систему без конфликтов тайминга.


Композиционные паттерны в интерфейсах

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

  • появление модальных окон: chain + stagger
  • интерактивные карточки: merge pointer + spring
  • списки с анимацией: stagger + transform
  • переходы экранов: parallel + tween + chain

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


Управление зависимостями между анимациями

Композиция также включает управление зависимостями:

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

Это формирует причинно-следственные связи внутри графа, где движение не просто параллельно, а логически связано.


Инверсия управления в композиции

Важный принцип — контроль времени передается не центральному таймеру, а самим потокам. Каждый компонент:

  • определяет свою динамику
  • сообщает состояние наружу
  • может быть встроен в более сложную систему

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