Эффект «рисования» SVG основан на управлении параметрами обводки пути. Векторная линия в SVG имеет вычисляемую длину, которая используется для создания иллюзии постепенного появления контура.
Ключевые свойства SVG:
stroke-dasharray — задаёт шаблон штриховки линииstroke-dashoffset — смещает начало отображаемого штриха
вдоль путиpathLength — нормализованная длина пути (необязательный
параметр, но полезный для упрощения расчётов)Базовый принцип DrawSVG заключается в следующем: вся длина линии
скрывается с помощью stroke-dashoffset, равного полной
длине пути, после чего значение анимируется до нуля.
Перед применением анимации SVG-путь должен быть корректно
подготовлен. Основное требование — наличие атрибута stroke
и отсутствие заливки.
<svg viewBox="0 0 400 200">
<path
id="line"
d="M10 100 C 100 10, 300 10, 390 100"
fill="none"
stroke="black"
stroke-width="2"
/>
</svg>
Дальнейшая работа строится вокруг вычисления длины пути:
const path = document.querySelector("#line");
const length = path.getTotalLength();
Этот метод возвращает реальную длину кривой, включая Bézier сегменты.
Библиотека Motion One предоставляет универсальный API
animate, который позволяет изменять любые числовые свойства
DOM и SVG.
Простейшая реализация эффекта рисования строится на анимации
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, easing: "ease-in-out" }
);
Логика работы:
strokeDashoffsetКорректный DrawSVG эффект требует строгой синхронизации двух параметров:
stroke-dasharray = длина путиstroke-dashoffset = длина пути → 0Расширенный вариант с динамическим вычислением:
import { animate } from "motion";
function drawSVG(path) {
const length = path.getTotalLength();
path.style.strokeDasharray = `${length}`;
path.style.strokeDashoffset = `${length}`;
return animate(
path,
{ strokeDashoffset: 0 },
{ duration: 1.5, easing: "ease-out" }
);
}
document.querySelectorAll("path").forEach(drawSVG);
Такой подход масштабируется на группы элементов без дополнительных зависимостей.
SVG-пути могут значительно различаться по длине, что приводит к несогласованной визуальной скорости анимации. Решение — нормализация длительности относительно длины.
import { animate } from "motion";
function drawSVG(path) {
const length = path.getTotalLength();
path.style.strokeDasharray = length;
path.style.strokeDashoffset = length;
const duration = Math.min(3, Math.max(0.8, length / 300));
return animate(
path,
{ strokeDashoffset: 0 },
{ duration, easing: "linear" }
);
}
Подход обеспечивает:
SVG-иллюстрации часто состоят из множества <path>
элементов. Для создания эффекта последовательного рисования применяется
каскадная анимация.
import { animate } from "motion";
const paths = document.querySelectorAll("svg path");
paths.forEach((path, index) => {
const length = path.getTotalLength();
path.style.strokeDasharray = length;
path.style.strokeDashoffset = length;
animate(
path,
{ strokeDashoffset: 0 },
{
duration: 1.2,
delay: index * 0.15,
easing: "ease-out"
}
);
});
Ключевая идея — использование delay, формирующего
временную шкалу без отдельного timeline-объекта.
Motion One поддерживает композицию анимаций через
timeline, что позволяет точно управлять последовательностью
отрисовки.
import { timeline } from "motion";
const paths = document.querySelectorAll("path");
const sequence = paths.map((path) => {
const length = path.getTotalLength();
path.style.strokeDasharray = length;
path.style.strokeDashoffset = length;
return [
path,
{ strokeDashoffset: 0 },
{ duration: 1 }
];
});
timeline(sequence);
Преимущество такого подхода:
Характер линии сильно зависит от функции сглаживания. Для DrawSVG наиболее применимы:
ease-out — быстрый старт, плавное завершениеease-in-out — равномерное распределение скоростиПример spring-модели:
import { animate } from "motion";
animate(path, {
strokeDashoffset: 0
}, {
type: "spring",
stiffness: 120,
damping: 20
});
Spring-подход создаёт «живое» поведение линии, особенно заметное на кривых.
SVG-отрисовка часто используется как скролл-индикатор или часть
интерактивных иллюстраций. В Motion One используется scroll
или inView механика.
import { animate, inView } from "motion";
const path = document.querySelector("path");
const length = path.getTotalLength();
path.style.strokeDasharray = length;
path.style.strokeDashoffset = length;
inView(path, () => {
animate(path, { strokeDashoffset: 0 }, { duration: 1.5 });
});
Для сложных сцен применяется контроль прогресса:
import { animate, scroll } from "motion";
const path = document.querySelector("path");
const length = path.getTotalLength();
path.style.strokeDasharray = length;
path.style.strokeDashoffset = length;
scroll(
animate(path, { strokeDashoffset: 0 }, { duration: 1 }),
{ target: path }
);
Такой подход связывает прогресс анимации с положением элемента в viewport.
При работе с большим количеством SVG-элементов возникает проблема производительности. Основные оптимизации:
Пример предварительной подготовки:
const paths = document.querySelectorAll("path");
paths.forEach((path) => {
const length = path.getTotalLength();
path.dataset.length = length;
path.style.strokeDasharray = length;
path.style.strokeDashoffset = length;
});
Далее анимация запускается без пересчёта геометрии.
Эффект рисования усиливается дополнительными трансформациями:
opacity)scale)translate)import { animate } from "motion";
animate(
path,
{
strokeDashoffset: 0,
opacity: 1,
transform: "translateY(0px)"
},
{
duration: 1.5,
easing: "ease-out"
}
);
Исходное состояние задаётся заранее:
path.style.opacity = 0;
path.style.transform = "translateY(10px)";
Так формируется многослойный эффект, в котором линия не только «рисуется», но и появляется в пространстве.
Для сложных SVG-сцен важна воспроизводимость анимации. Структура построения:
Пример унифицированного контроллера:
import { animate } from "motion";
function drawSequence(paths) {
let offset = 0;
paths.forEach((path) => {
const length = path.getTotalLength();
path.style.strokeDasharray = length;
path.style.strokeDashoffset = length;
animate(path, { strokeDashoffset: 0 }, {
duration: 1,
delay: offset,
easing: "ease-out"
});
offset += 0.2;
});
}
Такой метод позволяет управлять всей сценой как единым графическим процессом без внешних зависимостей.