Работа с динамически создаваемыми SVG-элементами в связке с Vivus позволяет строить интерфейсы, где графика не хранится заранее в виде статичных файлов, а формируется в момент выполнения скрипта. Такой подход особенно важен для визуализаций данных, интерактивных схем, генеративной графики и UI-компонентов, которые зависят от состояния приложения.
SVG в браузере создаётся через пространство имён XML. В отличие от
HTML-элементов, использование document.createElement здесь
неприменимо — требуется createElementNS.
const SVG_NS = "http://www.w3.org/2000/svg";
const svg = document.createElementNS(SVG_NS, "svg");
svg.setAttribute("width", "400");
svg.setAttribute("height", "200");
svg.setAttribute("viewBox", "0 0 400 200");
document.body.appendChild(svg);
Ключевой момент — обязательное указание namespace. Без него элементы будут интерпретироваться как HTML и не отрисуются корректно.
SVG строится из набора базовых элементов: path,
circle, rect, line,
polyline, polygon, text. Для
анимации с Vivus критически важны именно path, поскольку
библиотека работает с обводкой (stroke-dasharray /
stroke-dashoffset).
Пример динамического создания пути:
const path = document.createElementNS(SVG_NS, "path");
const d = "M 50 100 C 150 0, 250 200, 350 100";
path.setAttribute("d", d);
path.setAttribute("fill", "none");
path.setAttribute("stroke", "black");
path.setAttribute("stroke-width", "2");
svg.appendChild(path);
Здесь кривая Безье создаётся программно, а не загружается из файла.
SVG особенно удобен для процедурной графики. Например, синусоидальная линия:
let d = "M 0 100";
for (let x = 0; x <= 400; x += 10) {
const y = 100 + Math.sin(x * 0.05) * 40;
d += ` L ${x} ${y}`;
}
const wave = document.createElementNS(SVG_NS, "path");
wave.setAttribute("d", d);
wave.setAttribute("fill", "none");
wave.setAttribute("stroke", "blue");
svg.appendChild(wave);
Такие пути идеально подходят для последующей анимации через Vivus, поскольку представляют собой непрерывную линию.
Vivus работает с обводкой путей, постепенно «рисуя» их на экране. Для корректной работы требуется соблюдение нескольких условий:
fill должен быть отключён или не мешать восприятию
(fill="none")strokeДополнительно часто используется обнуление длины линии через CSS:
path {
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
}
Однако Vivus обычно управляет этим автоматически.
После генерации SVG можно подключить анимацию:
const vivus = new Vivus(svg, {
type: "delayed",
duration: 200,
animTimingFunction: Vivus.EASE
});
Ключевой момент — Vivus должен быть инициализирован после добавления SVG в DOM. Иначе библиотека не обнаружит элементы.
При динамической генерации часто возникает ситуация, когда SVG ещё не полностью построен в момент инициализации.
Решение — отложенный старт:
document.body.appendChild(svg);
requestAnimationFrame(() => {
new Vivus(svg, {
type: "oneByOne",
duration: 150
});
});
requestAnimationFrame гарантирует, что браузер завершит
DOM-обновление перед запуском анимации.
При изменении данных SVG может быть пересоздан полностью:
function rebuild() {
svg.innerHTML = "";
const path = document.createElementNS(SVG_NS, "path");
path.setAttribute("d", "M 0 50 L 400 150");
path.setAttribute("stroke", "black");
path.setAttribute("fill", "none");
svg.appendChild(path);
new Vivus(svg, {
type: "sync",
duration: 100
});
}
Каждое новое построение требует новой инициализации Vivus, так как внутренняя модель анимации не отслеживает динамические изменения структуры.
При сложных визуализациях SVG часто разбивается на группы
<g>:
const group = document.createElementNS(SVG_NS, "g");
for (let i = 0; i < 5; i++) {
const line = document.createElementNS(SVG_NS, "path");
const y = 20 + i * 30;
line.setAttribute("d", `M 0 ${y} L 400 ${y}`);
line.setAttribute("stroke", "black");
line.setAttribute("fill", "none");
group.appendChild(line);
}
svg.appendChild(group);
Vivus обрабатывает каждый path независимо, поэтому
группы удобно использовать для структурирования сцен.
SVG может строиться из массивов данных:
const data = [10, 40, 25, 60, 90];
const step = 80;
let d = "M 0 100";
data.forEach((value, index) => {
const x = index * step;
const y = 200 - value;
d += ` L ${x} ${y}`;
});
const graph = document.createElementNS(SVG_NS, "path");
graph.setAttribute("d", d);
graph.setAttribute("stroke", "red");
graph.setAttribute("fill", "none");
svg.appendChild(graph);
Такой подход позволяет строить графики, диаграммы и временные ряды, которые затем плавно прорисовываются Vivus.
Vivus предоставляет несколько режимов:
delayed — последовательная прорисовка сегментовsync — равномерная анимация всех путейoneByOne — поэлементная анимацияВ динамических SVG чаще используется sync, так как он
лучше подходит для непрерывно создаваемых форм.
new Vivus(svg, {
type: "sync",
duration: 300
});
При работе с большими сценами важны следующие принципы:
DocumentFragmentd-строкПример с фрагментом:
const fragment = document.createDocumentFragment();
for (let i = 0; i < 50; i++) {
const path = document.createElementNS(SVG_NS, "path");
path.setAttribute("d", `M 0 ${i * 5} L 400 ${i * 5}`);
path.setAttribute("stroke", "black");
path.setAttribute("fill", "none");
fragment.appendChild(path);
}
svg.appendChild(fragment);
При сложных трансформациях важно учитывать актуальную геометрию SVG:
const length = path.getTotalLength();
path.style.strokeDasharray = length;
path.style.strokeDashoffset = length;
Хотя Vivus делает это автоматически, ручной контроль используется при кастомных анимационных сценариях и нестандартных трансформациях путей.
SVG удобно использовать для генеративной графики:
for (let i = 0; i < 20; i++) {
const x1 = Math.random() * 400;
const y1 = Math.random() * 200;
const x2 = Math.random() * 400;
const y2 = Math.random() * 200;
const line = document.createElementNS(SVG_NS, "path");
line.setAttribute("d", `M ${x1} ${y1} L ${x2} ${y2}`);
line.setAttribute("stroke", "black");
line.setAttribute("fill", "none");
svg.appendChild(line);
}
Такие структуры создают основу для визуальных эффектов, где Vivus превращает случайные линии в последовательное «рисование».
При изменении SVG необходимо пересоздание экземпляра:
let instance;
function animate() {
if (instance) {
instance.stop().destroy();
}
instance = new Vivus(svg, {
type: "oneByOne",
duration: 120
});
}
Это связано с тем, что библиотека фиксирует список путей при создании и не отслеживает их дальнейшие изменения.
При сотнях элементов важно учитывать:
Часто применяется сегментация:
Такой подход позволяет сохранить плавность даже при высокой сложности сцены.