Векторная графика в SVG опирается на контур, который задаётся как
путь, линия или форма. Для управления визуализацией обводки используются
свойства stroke-dasharray и stroke-dashoffset,
позволяющие превращать сплошную линию в набор сегментов и управлять их
смещением вдоль пути.
Эти параметры особенно важны при создании анимаций «прорисовки», индикаторов прогресса и динамических графических эффектов, где линия как будто появляется постепенно.
stroke-dasharray определяет шаблон чередования штрихов и
пробелов вдоль контура.
Формально это список чисел, где:
Простейший пример:
<path d="M10 50 L300 50"
stroke="black"
stroke-width="4"
fill="none"
stroke-dasharray="10 5" />
В этом случае линия рисуется как:
Если задать одно значение:
stroke-dasharray="20"
формируется равномерное чередование:
Ключевое свойство поведения:
stroke-dashoffset смещает начало вычисления штрихов
относительно старта пути.
<path d="M10 50 L300 50"
stroke="black"
stroke-width="4"
fill="none"
stroke-dasharray="100"
stroke-dashoffset="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 предоставляет инструменты для управления числовыми
анимациями, которые идеально подходят для плавного изменения
stroke-dashoffset.
Ключевая идея:
stroke-dashoffset — числовой параметрПростейшая реализация с использованием 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-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;
}
});
Вместо линейного 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;
}
В динамических интерфейсах:
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, без перерасчёта
DOMrequestAnimationFrame (встроено в
Popmotion)getTotalLengthПри правильной организации: