Вложенные timeline

В библиотеке Popmotion временные последовательности (timeline) позволяют описывать сложные анимации как набор синхронизированных шагов. Вложенные timeline — это композиция нескольких временных последовательностей, где одна может содержать другие, образуя иерархическую структуру.

Такой подход решает несколько задач:

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

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


Базовый синтаксис timeline

Popmotion использует функцию timeline, принимающую массив шагов:

import { timeline } from "popmotion";

timeline([
  { track: "x", from: 0, to: 100, duration: 500 },
  { track: "opacity", from: 0, to: 1, duration: 300 }
]).start(v => console.log(v));

Каждый объект описывает:

  • track — имя анимационного канала
  • from / to — начальное и конечное значения
  • duration — длительность

Представление вложенности

Вложенные timeline формируются за счёт использования массивов внутри основного массива:

timeline([
  [
    { track: "x", from: 0, to: 100, duration: 500 },
    { track: "y", from: 0, to: 50, duration: 500 }
  ],
  [
    { track: "opacity", from: 0, to: 1, duration: 300 }
  ]
]);

Каждый вложенный массив представляет отдельный сегмент времени.

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

  • внешний массив — последовательность (по времени)
  • внутренние массивы — параллельные анимации

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

Последовательность

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

Анимации выполняются одна за другой.

Параллельность

timeline([
  [
    { track: "x", from: 0, to: 100, duration: 500 },
    { track: "opacity", from: 0, to: 1, duration: 500 }
  ]
]);

Все элементы внутри вложенного массива запускаются одновременно.


Глубокая вложенность

Вложенные timeline могут иметь несколько уровней:

timeline([
  [
    [
      { track: "x", from: 0, to: 100, duration: 300 },
      { track: "y", from: 0, to: 100, duration: 300 }
    ],
    { track: "opacity", from: 0, to: 1, duration: 300 }
  ],
  { track: "scale", from: 1, to: 1.5, duration: 200 }
]);

В этом примере:

  1. Внутренний блок (x и y) выполняется параллельно
  2. Затем вместе с ним запускается opacity
  3. После завершения всего блока начинается scale

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

Popmotion позволяет управлять временем запуска с помощью временных меток и смещений.

Использование delay

timeline([
  [
    { track: "x", from: 0, to: 100, duration: 500 },
    { track: "opacity", from: 0, to: 1, duration: 500, delay: 200 }
  ]
]);

opacity начнётся позже, несмотря на нахождение в одном блоке.


Относительные позиции

Можно использовать относительные временные позиции:

timeline([
  { track: "x", from: 0, to: 100, duration: 500 },
  "-200",
  { track: "opacity", from: 0, to: 1, duration: 300 }
]);

Значение "-200" означает, что следующая анимация начнётся на 200 мс раньше окончания предыдущей.


Композиция через функции

Для повышения переиспользуемости вложенные timeline удобно выносить в функции:

const fadeIn = () => ([
  { track: "opacity", from: 0, to: 1, duration: 300 }
]);

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

timeline([
  fadeIn(),
  moveRight()
]);

Это превращает timeline в модульную систему.


Переиспользование вложенных блоков

const bounce = () => ([
  { track: "y", from: 0, to: -50, duration: 200 },
  { track: "y", from: -50, to: 0, duration: 200 }
]);

timeline([
  bounce(),
  bounce(),
  bounce()
]);

Такой подход уменьшает дублирование кода и облегчает поддержку.


Синхронизация нескольких треков

Вложенные timeline позволяют точно синхронизировать разные свойства:

timeline([
  [
    { track: "x", from: 0, to: 200, duration: 600 },
    [
      { track: "scale", from: 1, to: 1.2, duration: 300 },
      { track: "scale", from: 1.2, to: 1, duration: 300 }
    ]
  ]
]);

Здесь:

  • движение по x длится 600 мс
  • масштабирование происходит внутри этого же временного окна

Работа с easing во вложенных структурах

Каждый элемент может иметь собственную функцию сглаживания:

import { easing } from "popmotion";

timeline([
  [
    {
      track: "x",
      from: 0,
      to: 100,
      duration: 500,
      ease: easing.easeIn
    },
    {
      track: "opacity",
      from: 0,
      to: 1,
      duration: 500,
      ease: easing.easeOut
    }
  ]
]);

Разные easing внутри одного блока позволяют создавать сложные визуальные эффекты.


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

Результат timeline передаётся в callback:

timeline([
  [
    { track: "x", from: 0, to: 100, duration: 500 },
    { track: "opacity", from: 0, to: 1, duration: 500 }
  ]
]).start(v => {
  element.style.transform = `translateX(${v.x}px)`;
  element.style.opacity = v.opacity;
});

Вложенность никак не влияет на структуру результата — значения агрегируются по трекам.


Динамическая генерация вложенных timeline

Вложенные структуры можно создавать программно:

const createSteps = (count) => {
  return Array.from({ length: count }, (_, i) => ({
    track: "x",
    from: i * 50,
    to: (i + 1) * 50,
    duration: 200
  }));
};

timeline([
  createSteps(5)
]);

Это особенно полезно при анимации списков и повторяющихся элементов.


Обработка сложных сценариев

Каскадные анимации

timeline([
  ...Array.from({ length: 5 }, (_, i) => ([
    { track: `item${i}`, from: 0, to: 1, duration: 300, delay: i * 100 }
  ]))
]);

Каждый элемент появляется с задержкой.


Комбинирование вложенности и смещений

timeline([
  [
    { track: "x", from: 0, to: 100, duration: 500 },
    "-250",
    { track: "opacity", from: 0, to: 1, duration: 500 }
  ],
  { track: "scale", from: 1, to: 2, duration: 300 }
]);

Здесь анимации частично перекрываются.


Типичные ошибки

1. Неправильное понимание вложенности

Вложенный массив — это не просто группировка, а параллельное выполнение.

2. Потеря контроля над временем

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

3. Смешивание логики и структуры

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


Практические рекомендации

  • ограничивать глубину вложенности (2–3 уровня)
  • использовать именованные функции для сложных блоков
  • явно задавать delay и временные смещения
  • группировать связанные анимации в один вложенный массив
  • тестировать каждый вложенный блок отдельно

Визуальная модель мышления

Вложенные timeline удобно представлять как:

  • внешний уровень — сценарий
  • внутренние уровни — сцены
  • объекты внутри — действия

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