Работа с группами и масками

В библиотеке Motion One групповые анимации строятся вокруг идеи одновременного управления набором DOM-элементов или последовательного их запуска с контролируемыми задержками. Основная модель основана на передаче массива элементов в функции animate, а также использовании параметров оркестрации (delay, stagger, at) и управления временем выполнения через timeline.

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

Анимация набора элементов

Базовый подход заключается в передаче коллекции DOM-узлов:

import { animate } from "motion"

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

animate(
  items,
  { opacity: [0, 1], transform: ["translateY(20px)", "translateY(0px)"] },
  { duration: 0.6 }
)

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

Управление независимостью элементов

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

const controls = animate(items, {
  opacity: 1,
  transform: "none"
})

controls.pause()
controls.play()

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


Stagger как основа групповой динамики

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

Линейный stagger

animate(
  ".item",
  { opacity: [0, 1], transform: ["scale(0.8)", "scale(1)"] },
  { delay: stagger(0.1) }
)

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

Геометрический stagger

При работе с сетками применяется зависимость задержки от позиции элемента:

animate(
  ".item",
  { opacity: [0, 1], transform: ["translateY(30px)", "translateY(0px)"] },
  {
    delay: stagger(0.05, { start: 0, from: "center" })
  }
)

Параметр from задаёт точку распространения анимации: центр, край или произвольный индекс.

Инвертированный порядок

stagger(0.08, { from: "last" })

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


Синхронизация групп через timeline

timeline используется для координации нескольких групп элементов и независимых анимационных сцен.

import { timeline } from "motion"

timeline([
  [".header", { opacity: [0, 1] }, { duration: 0.4 }],
  [".item", { y: [30, 0], opacity: [0, 1] }, { delay: stagger(0.05) }],
  [".footer", { opacity: [0, 1] }, { at: "+0.2" }]
])

Каждый шаг timeline представляет собой отдельную группу анимаций с собственной временной осью.

Относительное позиционирование групп

Параметр at позволяет задавать смещение относительно предыдущего шага:

  • at: 0 — старт одновременно с началом timeline
  • at: "+0.2" — задержка после завершения предыдущей группы
  • at: "<" — одновременный запуск с предыдущим шагом

Маскирование как инструмент групповой композиции

Маски применяются для управления видимостью группы элементов в процессе анимации. В контексте Motion One маски часто используются совместно с CSS-свойствами clip-path, mask-image и трансформациями контейнеров.

Clip-path как динамическая маска

animate(
  ".panel",
  {
    clipPath: [
      "inset(100% 0 0 0)",
      "inset(0% 0 0 0)"
    ]
  },
  { duration: 0.7 }
)

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

Маскирование через контейнерную анимацию

animate(
  ".list",
  { height: ["0px", "300px"] },
  { duration: 0.5 }
)

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


SVG-маски и групповая анимация

SVG предоставляет более гибкий уровень контроля над группами через <mask> и <clipPath>.

Анимация группы внутри SVG-маски

animate(
  "#maskRect",
  { width: ["0%", "100%"] },
  { duration: 1 }
)

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

Координация нескольких масок

timeline([
  ["#mask1", { width: ["0%", "100%"] }, { duration: 0.5 }],
  ["#mask2", { height: ["0%", "100%"] }, { at: "-0.2" }]
])

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


Группы и трансформационные контейнеры

DOM-группа часто рассматривается как единый трансформационный слой. В Motion One это достигается через анимацию родительского элемента.

Масштабирование группы

animate(
  ".card-group",
  { scale: [0.8, 1], opacity: [0, 1] },
  { duration: 0.5 }
)

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

Комбинированные трансформации

animate(
  ".grid",
  {
    rotate: ["-2deg", "0deg"],
    scale: [0.95, 1],
    opacity: [0, 1]
  },
  { duration: 0.6 }
)

Использование комбинированных свойств усиливает ощущение целостности группы.


Оркестрация групповых последовательностей

Оркестрация определяет порядок запуска анимаций внутри и между группами.

Последовательная оркестрация

animate(
  ".item",
  { x: [-20, 0], opacity: [0, 1] },
  { delay: stagger(0.08) }
)

Элементы запускаются последовательно, образуя каскад.

Параллельные группы

timeline([
  [".left-column", { opacity: [0, 1] }],
  [".right-column", { opacity: [0, 1] }, { at: 0 }]
])

Обе группы стартуют одновременно, но могут иметь разные внутренние stagger-параметры.


Маскирование через opacity и scale

Вместо геометрических масок часто применяется имитация маскирования через прозрачность и масштабирование.

Fade-маска группы

animate(
  ".item",
  { opacity: [0, 1], filter: ["blur(6px)", "blur(0px)"] },
  { delay: stagger(0.05) }
)

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

Маска через scale

animate(
  ".popup",
  { scale: [0.7, 1], opacity: [0, 1] },
  { duration: 0.4 }
)

Сочетание масштабирования и прозрачности создаёт иллюзию раскрытия пространства.


Работа с динамическими группами

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

Анимация обновляемых списков

const renderItems = () => {
  const items = document.querySelectorAll(".item")

  animate(
    items,
    { opacity: [0, 1], y: [10, 0] },
    { delay: stagger(0.03) }
  )
}

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

Пересоздание групп

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


Комбинирование групп и масок в интерфейсах

Наиболее сложные сценарии возникают при наложении групповых анимаций и масок.

Вложенные группы с маскированием

timeline([
  [".modal", { opacity: [0, 1] }, { duration: 0.3 }],
  [
    ".modal-content .item",
    { y: [20, 0], opacity: [0, 1] },
    { delay: stagger(0.05) }
  ]
])

Контейнер анимируется как маска, а вложенные элементы формируют внутреннюю структуру появления.

Маска + stagger

animate(
  ".card",
  {
    clipPath: ["inset(100% 0 0 0)", "inset(0% 0 0 0)"],
    opacity: [0, 1]
  },
  { delay: stagger(0.06) }
)

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


Производительность групповых анимаций

Групповые операции оптимизируются через минимизацию layout-thrashing и использование transform/opacity как основных анимируемых свойств.

Рекомендации по вычислительной нагрузке

  • предпочтение transform и opacity
  • минимизация анимации layout-свойств (width, height)
  • использование контейнерных трансформаций вместо анимации каждого элемента
  • применение stagger вместо индивидуальных таймеров

Паттерны сложных групповых сцен

Каскадная сетка

animate(
  ".grid-item",
  { opacity: [0, 1], scale: [0.9, 1] },
  { delay: stagger(0.02, { from: "center" }) }
)

Радиальное раскрытие

animate(
  ".dot",
  { scale: [0, 1], opacity: [0, 1] },
  { delay: stagger(0.03, { from: "first" }) }
)

Параллельные маски

timeline([
  [".mask-left", { width: ["0%", "50%"] }],
  [".mask-right", { width: ["0%", "50%"] }, { at: 0 }]
])

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


Композиция нескольких уровней групп

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

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

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