Stagger-анимации

Stagger-анимации представляют собой последовательное применение анимационных эффектов к группе элементов с контролируемой задержкой между каждым запуском. В экосистеме Velocity.js подобный механизм является одним из ключевых инструментов построения динамичных интерфейсов, позволяя превращать массовые DOM-операции в визуально структурированные и предсказуемые последовательности.

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

В Velocity.js это достигается через параметр задержки, который интерпретируется не как фиксированная пауза, а как функция распределения времени между элементами.

Основная форма применения:

Velocity(elements, {
  opacity: 1,
  translateY: "0px"
}, {
  duration: 400,
  stagger: 100
});

Каждый элемент из коллекции elements стартует с интервалом 100 миллисекунд относительно предыдущего.

Математика распределения задержек

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

t(i) = t0 + i * Δ

где:

  • t(i) — время старта анимации элемента с индексом i
  • t0 — начальная задержка
  • Δ — шаг задержки

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

Velocity(elements, {
  scale: 1
}, {
  duration: 500,
  stagger: function(elements, index) {
    return index * 80;
  }
});

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

Использование массива элементов

Stagger-анимации наиболее эффективно работают с массивами DOM-узлов, полученными через селекторы или коллекции:

const items = document.querySelectorAll(".item");

Velocity(items, {
  opacity: 1,
  translateX: "0px"
}, {
  duration: 300,
  stagger: 50
});

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

Линейный, обратный и центрированный stagger

Распределение задержек может изменять направление последовательности.

Прямой порядок

stagger: 80

Анимация идёт от первого элемента к последнему.

Обратный порядок

stagger: [80, true]

Флаг true инвертирует последовательность, начиная с конца массива.

Центрированный stagger

Центрированный подход формирует симметричную волну анимации:

Velocity(items, {
  opacity: 1
}, {
  duration: 400,
  stagger: {
    amount: 600,
    from: "center"
  }
});

Такой режим создаёт эффект распространения анимации от центра контейнера к краям.

Контроль общей длительности

При большом количестве элементов линейный stagger может растянуть анимацию на значительное время. Для управления общей длительностью используется параметр amount, распределяющий суммарное время между всеми элементами:

Velocity(items, {
  translateY: "0px"
}, {
  duration: 300,
  stagger: {
    amount: 1000
  }
});

Каждый элемент получает свою долю в пределах общего временного окна.

Easing в stagger-анимациях

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

Velocity(items, {
  opacity: 1
}, {
  duration: 500,
  easing: "easeOutQuad",
  stagger: 70
});

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

Функциональный stagger

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

Velocity(items, {
  scale: 1
}, {
  duration: 400,
  stagger: function(elements, index, total) {
    return Math.sin(index / total * Math.PI) * 120;
  }
});

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

Комбинация с очередями анимации

Velocity.js поддерживает систему очередей, где stagger-анимации могут быть встроены в цепочки переходов:

Velocity(items, { opacity: 0 }, { duration: 0 });

Velocity(items, {
  opacity: 1,
  translateY: "0px"
}, {
  duration: 300,
  stagger: 60,
  queue: true
});

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

Перекрытие и пересечение анимаций

При использовании stagger в сочетании с одновременными анимациями отдельных элементов важно учитывать конкуренцию свойств. Если несколько вызовов Velocity изменяют одно и то же CSS-свойство, применяется приоритет последнего вызова в очереди.

Velocity(items, { opacity: 0.5 }, { stagger: 50 });

Velocity(items, { opacity: 1 }, { stagger: 50 });

В подобных случаях итоговое состояние зависит от порядка выполнения.

Производительность при большом количестве элементов

При масштабировании до сотен и тысяч DOM-узлов stagger-анимации становятся чувствительными к нагрузке. Основные факторы:

  • частота вызовов layout/reflow
  • сложность CSS-свойств (transform предпочтительнее opacity и top/left)
  • количество одновременно активных таймеров

Оптимизированный подход:

Velocity(items, {
  translate3d: ["0px", "0px", "0px"]
}, {
  duration: 250,
  stagger: 20,
  queue: false
});

Использование translate3d переносит вычисления в GPU, снижая нагрузку на основной поток.

Сочетание stagger с динамическими списками

При работе с динамически добавляемыми элементами stagger требует пересчёта коллекции на момент запуска анимации. Фиксированные NodeList могут не учитывать новые элементы, поэтому предпочтительно формировать выборку непосредственно перед вызовом:

function animateList() {
  const items = document.querySelectorAll(".item");

  Velocity(items, {
    opacity: 1
  }, {
    duration: 300,
    stagger: 40
  });
}

Взаимодействие с пользовательскими событиями

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

button.addEventListener("click", () => {
  Velocity(items, {
    scale: 1.1
  }, {
    duration: 200,
    stagger: 30
  });
});

В таком режиме формируется эффект «распространяющегося отклика» интерфейса.

Композиция нескольких stagger-слоёв

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

Velocity(items, {
  opacity: 1
}, {
  duration: 300,
  stagger: 40
});

Velocity(items, {
  translateY: "0px"
}, {
  duration: 500,
  stagger: 80
});

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