Сложные последовательности

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

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

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

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

Последовательное выполнение анимаций

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

import { animate, sequence } from 'popmotion';

sequence([
  (complete) => animate({
    from: 0,
    to: 100,
    duration: 300,
    onUpdate: (v) => console.log('A:', v),
    onComplete: complete
  }),

  (complete) => animate({
    from: 100,
    to: 200,
    duration: 300,
    onUpdate: (v) => console.log('B:', v),
    onComplete: complete
  })
]);

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

Параллельные группы и синхронизация

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

import { animate, parallel } from 'popmotion';

parallel([
  (complete) => animate({
    from: 0,
    to: 300,
    duration: 600,
    onUpdate: v => console.log('X:', v),
    onComplete: complete
  }),

  (complete) => animate({
    from: 0,
    to: 1,
    duration: 600,
    onUpdate: v => console.log('Opacity:', v),
    onComplete: complete
  })
]);

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

Каскадные эффекты и задержки

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

import { animate, delay } from 'popmotion';

delay(100)
  .start(() => {
    animate({
      from: 0,
      to: 50,
      duration: 300
    });
  });

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

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

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

import { animate } from 'popmotion';

const fadeIn = (complete) =>
  animate({
    from: 0,
    to: 1,
    duration: 200,
    onComplete: complete
  });

const slideUp = (complete) =>
  animate({
    from: 50,
    to: 0,
    duration: 300,
    onComplete: complete
  });

const runSequence = () => {
  fadeIn(() => {
    slideUp(() => {
      console.log('done');
    });
  });
};

runSequence();

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

Смешанные модели движения

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

import { animate, spring } from 'popmotion';

const runMixed = (complete) => {
  animate({
    from: 0,
    to: 100,
    duration: 200,
    onComplete: () => {
      spring({
        from: 100,
        to: 0,
        stiffness: 120,
        damping: 20,
        onComplete: complete
      }).start();
    }
  });
};

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

Управление временем исполнения

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

import { animate } from 'popmotion';

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

setTimeout(() => {
  animation.stop();
}, 400);

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

Динамические последовательности

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

import { animate } from 'popmotion';

const createStep = (start, end, next) => (complete) =>
  animate({
    from: start,
    to: end,
    duration: 250,
    onComplete: () => next && next(complete)
  });

const sequence = createStep(0, 50,
  createStep(50, 150,
    createStep(150, 0)
  )
);

sequence(() => {
  console.log('dynamic chain complete');
});

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

Параллельно-последовательные гибриды

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

import { animate } from 'popmotion';

const step1 = (complete) => {
  animate({
    from: 0,
    to: 100,
    duration: 300,
    onComplete: () => {
      animate({
        from: 0,
        to: 1,
        duration: 200,
        onComplete: complete
      });
    }
  });
};

const step2 = (complete) => {
  let done = 0;

  const check = () => {
    done++;
    if (done === 2) complete();
  };

  animate({ from: 0, to: 50, duration: 400, onComplete: check });
  animate({ from: 50, to: 0, duration: 400, onComplete: check });
};

const run = (done) => {
  step1(() => {
    step2(done);
  });
};

run(() => console.log('all complete'));

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

Контроль завершения и согласование состояния

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

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

Масштабирование анимационных графов

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

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

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

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