Stroke dashoffset и dasharray

Векторная графика в SVG опирается на контур, который задаётся как путь, линия или форма. Для управления визуализацией обводки используются свойства stroke-dasharray и stroke-dashoffset, позволяющие превращать сплошную линию в набор сегментов и управлять их смещением вдоль пути.

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


stroke-dasharray: структура штриховки линии

stroke-dasharray определяет шаблон чередования штрихов и пробелов вдоль контура.

Формально это список чисел, где:

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

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

<path d="M10 50 L300 50"
      stroke="black"
      stroke-width="4"
      fill="none"
      stroke-dasharray="10 5" />

В этом случае линия рисуется как:

  • 10 единиц — видимый участок
  • 5 единиц — пропуск

Если задать одно значение:

stroke-dasharray="20"

формируется равномерное чередование:

  • 20 единиц линия
  • 20 единиц пробел

Ключевое свойство поведения:

  • сумма значений определяет «ритм» повторения
  • массив повторяется бесконечно по всей длине пути

stroke-dashoffset: сдвиг шаблона вдоль пути

stroke-dashoffset смещает начало вычисления штрихов относительно старта пути.

<path d="M10 50 L300 50"
      stroke="black"
      stroke-width="4"
      fill="none"
      stroke-dasharray="100"
      stroke-dashoffset="50" />

В этом случае:

  • весь шаблон сдвигается на 50 единиц
  • визуально создаётся эффект «съезда» линии

Главная особенность:

  • отрицательные значения двигают рисунок в противоположную сторону
  • положительные — в стандартную

Механика эффекта прорисовки линии

Классическая техника анимации SVG-пути строится на комбинации:

  • stroke-dasharray = длина всего пути
  • stroke-dashoffset = длина пути (скрывает линию полностью)
  • а затем постепенное уменьшение stroke-dashoffset до 0

Иллюстрация логики:

<path id="line"
      d="M10 50 C100 10, 200 90, 300 50"
      stroke="black"
      stroke-width="3"
      fill="none"/>
const path = document.querySelector("#line");
const length = path.getTotalLength();

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

На старте линия полностью скрыта, так как пробел равен всей длине пути.


Интеграция с Popmotion

Popmotion предоставляет инструменты для управления числовыми анимациями, которые идеально подходят для плавного изменения stroke-dashoffset.

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

  • stroke-dashoffset — числовой параметр
  • Popmotion умеет интерполировать числа во времени
  • SVG реагирует на изменения стиля напрямую

Анимация прорисовки через tween

Простейшая реализация с использованием tween-подхода:

import { animate } from "popmotion";

const path = document.querySelector("#line");
const length = path.getTotalLength();

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

animate({
  from: length,
  to: 0,
  duration: 2000,
  onUpdate: (v) => {
    path.style.strokeDashoffset = v;
  }
});

Поведение:

  • значение стартует с полной длины
  • постепенно уменьшается до нуля
  • создаётся эффект «рисования линии»

Управление скоростью и кривыми движения

Popmotion позволяет управлять не только линейной интерполяцией, но и характером изменения значения через easing-функции.

Пример с ускорением и замедлением:

import { animate, easeInOut } from "popmotion";

animate({
  from: length,
  to: 0,
  duration: 2500,
  ease: easeInOut,
  onUpdate: (v) => {
    path.style.strokeDashoffset = v;
  }
});

Влияние easing:

  • easeIn — медленный старт, быстрый конец
  • easeOut — быстрый старт, замедление
  • easeInOut — симметричное движение

Связка stroke-dasharray и динамического прогресса

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

  • stroke-dasharray фиксирован
  • stroke-dashoffset зависит от процента выполнения

Формула поведения:

offset = length - (length * progress)

Где progress находится в диапазоне от 0 до 1.

Пример:

import { animate } from "popmotion";

const circle = document.querySelector("#circle");
const length = circle.getTotalLength();

circle.style.strokeDasharray = length;

animate({
  from: 0,
  to: 1,
  duration: 3000,
  onUpdate: (p) => {
    circle.style.strokeDashoffset = length - length * p;
  }
});

Использование spring-динамики

Вместо линейного tween можно применять физическую модель пружины.

import { animate, spring } from "popmotion";

animate({
  from: length,
  to: 0,
  ease: spring({ stiffness: 120, damping: 15 }),
  onUpdate: (v) => {
    path.style.strokeDashoffset = v;
  }
});

Характер движения:

  • сначала быстрое ускорение
  • затем колебательное затухание
  • естественное поведение без фиксированной скорости

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

При работе с несколькими SVG-элементами важно учитывать независимость их длин:

const paths = document.querySelectorAll("path");

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

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

  animate({
    from: length,
    to: 0,
    delay: i * 200,
    duration: 1500,
    onUpdate: (v) => {
      path.style.strokeDashoffset = v;
    }
  });
});

Эффект:

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

Динамическое изменение формы и длины пути

Если SVG-путь изменяет форму во время анимации, его длина также меняется. Это требует пересчёта:

function updateLength(path) {
  const length = path.getTotalLength();
  path.style.strokeDasharray = length;
  return length;
}

В динамических интерфейсах:

  • пересчёт выполняется при resize
  • или при morph-анимациях
  • или при смене состояния UI

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

stroke-dashoffset часто используется вместе с:

  • transform: rotate()
  • scale()
  • translate()

Пример синхронизации:

animate({
  from: 0,
  to: 1,
  duration: 2000,
  onUpdate: (t) => {
    path.style.strokeDashoffset = length - length * t;
    path.style.transform = `rotate(${t * 360}deg)`;
  }
});

В результате:

  • линия рисуется
  • одновременно вращается
  • создаётся эффект динамического построения формы

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

Индикаторы загрузки

  • круговые прогресс-бары
  • линейные полосы
  • кольцевые диаграммы

Интерактивные схемы

  • подсветка маршрутов
  • пошаговая визуализация алгоритмов

UI-анимации

  • появление иконок
  • акцентирование элементов интерфейса
  • декоративные эффекты наведения

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

При интенсивных анимациях SVG важно учитывать:

  • обновление только stroke-dashoffset, без перерасчёта DOM
  • избегание layout thrashing
  • использование requestAnimationFrame (встроено в Popmotion)
  • минимизация повторных вызовов getTotalLength

При правильной организации:

  • анимация остаётся стабильной
  • нагрузка на CPU минимальна
  • поведение предсказуемо даже при множестве элементов