Создание последовательностей из сегментов

В основе управления проигрыванием в Lottie Web лежит работа с диапазонами кадров, которые формируют логические отрезки анимации. Сегмент представляет собой пару значений [startFrame, endFrame], ограничивающих часть композиции, которую необходимо воспроизвести.

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

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

Каждый сегмент задаётся двумя числами:

  • начальный кадр
  • конечный кадр

Внутренне Lottie интерпретирует их как диапазон временной шкалы композиции. Пример структуры:

const segment = [0, 30];

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

Основной механизм воспроизведения сегментов

В Lottie Web управление сегментами осуществляется через метод:

animation.playSegments([from, to], forceFlag);

где:

  • from — начальный кадр
  • to — конечный кадр
  • forceFlag — принудительный запуск (обрывает текущее воспроизведение)

Простейший пример:

animation.playSegments([0, 25], true);

Такой вызов инициирует проигрывание только указанного диапазона.

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

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

Базовая схема последовательного воспроизведения

const segments = [
  [0, 30],
  [30, 60],
  [60, 90]
];

Для последовательного запуска применяется обработчик завершения:

let index = 0;

function playNextSegment() {
  if (index >= segments.length) return;

  const segment = segments[index];

  animation.playSegments(segment, true);
}

animation.addEventListener('complete', () => {
  index++;
  playNextSegment();
});

playNextSegment();

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

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

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

animation.playSegments([60, 30], true);

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

Построение очередей сегментов

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

class SegmentQueue {
  constructor(animation, segments) {
    this.animation = animation;
    this.segments = segments;
    this.current = 0;

    this.onCompl ete = this.onComplete.bind(this);
    this.animation.addEventListener('complete', this.onComplete);
  }

  start() {
    this.current = 0;
    this.play();
  }

  play() {
    const segment = this.segments[this.current];
    this.animation.playSegments(segment, true);
  }

  onComplete() {
    this.current++;

    if (this.current < this.segments.length) {
      this.play();
    }
  }
}

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

const queue = new SegmentQueue(animation, [
  [0, 20],
  [20, 50],
  [50, 120]
]);

queue.start();

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

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

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

Сброс очереди

queue.current = 0;
animation.stop();

Замена оставшихся сегментов

queue.segments = [
  [10, 40],
  [40, 70]
];

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

Сегменты как состояния анимации

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

Пример структуры состояний:

const states = {
  idle: [0, 15],
  loading: [15, 45],
  success: [45, 75],
  error: [75, 105]
};

Переход между состояниями:

function setState(state) {
  animation.playSegments(states[state], true);
}

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

Асинхронное управление сегментами

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

function playSegment(animation, segment) {
  return new Promise(resolve => {
    function handler() {
      animation.removeEventListener('complete', handler);
      resolve();
    }

    animation.addEventListener('complete', handler);
    animation.playSegments(segment, true);
  });
}

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

async function runSequence() {
  await playSegment(animation, [0, 30]);
  await playSegment(animation, [30, 60]);
  await playSegment(animation, [60, 90]);
}

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

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

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

const sequence = [
  {
    name: 'intro',
    segments: [
      [0, 20],
      [20, 40]
    ]
  },
  {
    name: 'main',
    segments: [
      [40, 80],
      [80, 120]
    ]
  }
];

Обход структуры:

async function playComplexSequence(sequence) {
  for (const group of sequence) {
    for (const segment of group.segments) {
      await playSegment(animation, segment);
    }
  }
}

Синхронизация с внешними событиями

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

button.addEventListener('click', () => {
  animation.playSegments([0, 25], true);
});

form.addEventListener('submit', () => {
  animation.playSegments([25, 60], true);
});

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

Повторное использование сегментов

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

const segments = {
  open: [0, 30],
  hold: [30, 60],
  close: [60, 90]
};

function openAnimation() {
  animation.playSegments(segments.open, true);
}

function closeAnimation() {
  animation.playSegments(segments.close, true);
}

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

Контроль завершения и переходов

Ключевым аспектом при работе с последовательностями является контроль момента завершения сегмента. Основное событие:

animation.addEventListener('complete', callback);

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

Распространённый подход — введение флага активности:

let isSegmentMode = false;

function play(segment) {
  isSegmentMode = true;
  animation.playSegments(segment, true);
}

animation.addEventListener('complete', () => {
  if (!isSegmentMode) return;

  // переход к следующему сегменту
});

Сегментная маршрутизация

При росте количества сегментов удобно рассматривать их как граф переходов:

const graph = {
  start: { segment: [0, 20], next: 'loading' },
  loading: { segment: [20, 60], next: 'success' },
  success: { segment: [60, 100], next: null }
};

Исполнение:

let current = 'start';

function playNode(name) {
  const node = graph[name];

  animation.playSegments(node.segment, true);

  animation.addEventListener('complete', function handler() {
    animation.removeEventListener('complete', handler);

    if (node.next) {
      playNode(node.next);
    }
  });
}

playNode(current);

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

Смешивание сегментов с зацикливанием

Некоторые сегменты требуют бесконечного повторения до наступления события:

animation.loop = true;
animation.playSegments([30, 60], true);

В этом случае завершение сегмента не наступает в привычном смысле, и управление переходами должно учитывать состояние loop.

Практика комбинирования сегментов и скорости

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

animation.setSpeed(1.5);
animation.playSegments([0, 50], true);

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

Итеративное построение сложных последовательностей

При построении больших анимационных сценариев сегменты объединяются в многоуровневые конструкции:

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

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