Stroke animations

Анимации обводки в SVG основаны на управлении параметрами контура: stroke-dasharray и stroke-dashoffset. Эти свойства позволяют имитировать эффект постепенного прорисовывания линии, когда путь как будто рисуется от начала до конца.

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

Базовая модель обводки

SVG-путь можно представить как непрерывную линию фиксированной длины. Для управления её отображением используются два параметра:

  • stroke-dasharray — определяет шаблон штрихов (длина видимых и скрытых сегментов)
  • stroke-dashoffset — смещает начало шаблона вдоль пути

Если задать:

  • stroke-dasharray = L
  • stroke-dashoffset = L

линия становится полностью скрытой. При анимации stroke-dashoffset от L до 0 создаётся эффект постепенного появления линии.

Motion One и работа с SVG

Motion One предоставляет минималистичный API для анимаций, включая SVG-атрибуты. В отличие от более тяжёлых решений, библиотека опирается на нативные браузерные возможности: requestAnimationFrame, CSS transforms и SVG presentation attributes.

Основная функция — animate():

import { animate } from "motion"

Она позволяет анимировать как CSS-свойства, так и SVG-атрибуты напрямую.

Подготовка SVG для stroke-анимации

Для корректной работы эффекта рисования необходимо учитывать структуру path:

<svg viewBox="0 0 200 200">
  <path
    d="M10 100 C 40 10, 65 10, 95 80 S 150 150, 180 100"
    stroke="black"
    fill="none"
    stroke-width="2"
  />
</svg>

Ключевое требование — отсутствие заливки (fill="none"), так как анимация касается только обводки.

Вычисление длины пути

Для создания эффекта рисования требуется знать полную длину пути. Это делается через метод:

const path = document.querySelector("path")
const length = path.getTotalLength()

Полученное значение используется для настройки dash-параметров.

Базовая stroke-анимация в Motion One

Простейший эффект прорисовки реализуется через анимацию strokeDashoffset:

import { animate } from "motion"

const path = document.querySelector("path")
const length = path.getTotalLength()

path.style.strokeDasharray = length
path.style.strokeDashoffset = length

animate(path, { strokeDashoffset: 0 }, { duration: 2 })

В этом сценарии Motion One интерполирует числовое значение от length до 0, создавая эффект рисования линии.

Использование start offset и контроль направления

Изменение направления анимации достигается инверсией значений:

path.style.strokeDasharray = length
path.style.strokeDashoffset = 0

animate(path, { strokeDashoffset: length }, { duration: 2 })

Таким образом линия «стирается» вместо рисования.

Анимация через атрибут pathLength

Motion One поддерживает альтернативный подход — использование pathLength, который абстрагирует работу с реальной длиной SVG.

animate(path, { pathLength: [0, 1] }, { duration: 2 })

В этом случае библиотека автоматически нормализует длину пути, переводя значения в диапазон от 0 до 1. Это упрощает работу и устраняет необходимость вызова getTotalLength().

Сравнение подходов

strokeDashoffset

Преимущества:

  • Полный контроль над SVG
  • Совместимость с любыми эффектами dash-pattern

Недостатки:

  • Требуется вычисление длины пути
  • Более низкоуровневая логика

pathLength

Преимущества:

  • Абстракция над длиной пути
  • Простая декларативная модель
  • Удобство в анимационных сценах

Недостатки:

  • Меньше гибкости при сложных dash-эффектах

Комбинирование stroke-анимации с easing

Motion One предоставляет встроенные функции сглаживания движения. Они влияют на скорость прорисовки линии.

import { animate, easeInOut } from "motion"

animate(
  path,
  { strokeDashoffset: 0 },
  {
    duration: 2,
    easing: easeInOut
  }
)

Разные easing-функции создают различное восприятие рисования:

  • linear — равномерное появление
  • ease-in — ускорение в конце
  • ease-out — замедление в конце
  • ease-in-out — мягкая динамика

Последовательная анимация нескольких путей

SVG часто содержит несколько path-элементов. Motion One позволяет управлять ими независимо или группировать анимации.

const paths = document.querySelectorAll("path")

paths.forEach((path, index) => {
  const length = path.getTotalLength()

  path.style.strokeDasharray = length
  path.style.strokeDashoffset = length

  animate(
    path,
    { strokeDashoffset: 0 },
    { duration: 1.5, delay: index * 0.2 }
  )
})

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

Синхронизация stroke и transform

SVG-анимации часто комбинируются с трансформациями для усиления визуального эффекта.

animate(
  path,
  {
    strokeDashoffset: 0,
    scale: [0.9, 1]
  },
  {
    duration: 2
  }
)

Совмещение трансформации и прорисовки позволяет создать эффект появления объекта из линии.

Динамическое управление прогрессом анимации

Motion One поддерживает управление прогрессом через контролируемые анимации.

const animation = animate(
  path,
  { strokeDashoffset: 0 },
  { duration: 3, autoplay: false }
)

animation.currentTime = 1500

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

Привязка к скроллу

Stroke-анимации часто используются для иллюстраций, реагирующих на прокрутку.

import { animate, scroll } from "motion"

scroll(
  animate(path, { strokeDashoffset: 0 }),
  { target: path }
)

В этом режиме прогресс анимации зависит от позиции элемента в viewport.

Работа с несколькими слоями stroke

Сложные иллюстрации часто состоят из нескольких слоёв:

  • основной контур
  • декоративные линии
  • вторичные акценты

Каждый слой можно анимировать с разными параметрами:

animate(primaryPath, { strokeDashoffset: 0 }, { duration: 2 })

animate(secondaryPath, { strokeDashoffset: 0 }, { duration: 2.5, delay: 0.3 })

Разница в таймингах создаёт ощущение глубины и сложности рисунка.

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

SVG stroke-анимации чувствительны к количеству узлов в path. Чем больше сегментов, тем выше нагрузка на перерисовку.

Практика оптимизации:

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

Motion One минимизирует накладные расходы, но сложность SVG остаётся критическим фактором.

Сочетание stroke и opacity

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

animate(
  path,
  {
    strokeDashoffset: 0,
    opacity: [0, 1]
  },
  {
    duration: 1.8
  }
)

Появление линии становится более мягким и менее механическим.

Инверсные эффекты

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

animate(
  path,
  { strokeDashoffset: length },
  { duration: 2 }
)

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

Использование переменных CSS в связке с Motion One

Motion One поддерживает работу с CSS custom properties, что позволяет управлять stroke через переменные.

document.documentElement.style.setProperty("--dash", length)

animate(path, { strokeDashoffset: "var(--dash)" }, { duration: 0 })

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

Масштабирование stroke-анимаций в интерфейсах

Stroke-анимации используются в:

  • интерактивных диаграммах
  • onboarding-иллюстрациях
  • визуализации маршрутов
  • индикаторах прогресса
  • реактивной графике

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

Паттерны построения сложных анимаций

Типовые архитектуры stroke-анимаций:

Линейный рисунок

Последовательное появление path без задержек и ветвлений.

Каскадный рисунок

Группы path с разными задержками и длительностью.

Интерактивный рисунок

Прогресс зависит от событий: hover, scroll, pointermove.

Циклический рисунок

Повторяющаяся анимация с reset состояния strokeDashoffset.

animate(
  path,
  { strokeDashoffset: length },
  {
    duration: 2,
    repeat: Infinity
  }
)

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