Анимация SVG с использованием Vivus требует наличия разметки SVG
непосредственно в DOM. Внешний файл не может быть анимирован напрямую,
поскольку библиотека работает с внутренними путями
<path>, изменяя их свойства
(stroke-dasharray, stroke-dashoffset). Поэтому
первый этап загрузки SVG через AJAX всегда связан с получением
текстового содержимого файла и последующей вставкой его в документ.
Базовый запрос может быть выполнен через fetch, который
возвращает исходный SVG как строку:
fetch('/assets/icon.svg')
.then(response => response.text())
.then(svgText => {
document.querySelector('#svg-container').innerHTML = svgText;
});
После вставки SVG становится частью DOM-дерева, и только теперь становится возможной его обработка Vivus.
Классическая ошибка при работе с динамически загруженными SVG —
попытка инициализировать анимацию до завершения вставки. В результате
Vivus не находит элементов <path> и не выполняет
анимацию.
После получения строки SVG важно корректно интегрировать её в документ. Обычно используется контейнерный элемент:
<div id="svg-container"></div>
И вставка происходит напрямую:
function loadSVG(url, container) {
return fetch(url)
.then(res => res.text())
.then(svg => {
container.innerHTML = svg;
return container.querySelector('svg');
});
}
Ключевой момент — сохранение ссылки на вставленный
<svg> элемент. Именно он передаётся в Vivus для
последующей анимации.
После того как SVG становится частью DOM, можно запускать анимацию:
loadSVG('/assets/diagram.svg', document.getElementById('svg-container'))
.then(svg => {
new Vivus(svg, {
type: 'delayed',
duration: 200,
start: 'autostart'
});
});
Здесь важно, что первым аргументом может быть как id
элемента, так и сам DOM-узел. При динамической загрузке предпочтительнее
передавать именно узел, так как идентификаторы часто отсутствуют или
создаются на лету.
Несмотря на распространение fetch, в старых проектах
часто используется XMLHttpRequest, который обеспечивает
аналогичную функциональность:
function loadSVGXHR(url, callback) {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatecha nge = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
const container = document.getElementById('svg-container');
container.innerHTML = xhr.responseText;
const svg = container.querySelector('svg');
callback(svg);
}
};
xhr.send();
}
После загрузки SVG дальнейшая логика идентична — передача элемента в Vivus.
При попытке загрузить SVG с другого домена возникает ограничение политики CORS. Сервер должен возвращать заголовки, разрешающие доступ:
Access-Control-Allow-Origin: *
Иначе браузер блокирует ответ, и SVG не может быть вставлен в DOM.
Если контроль над сервером отсутствует, единственным вариантом остаётся проксирование файла через собственный backend.
Надёжная реализация всегда включает обработку ошибок сети:
fetch('/assets/icon.svg')
.then(res => {
if (!res.ok) throw new Error('HTTP error');
return res.text();
})
.then(svg => {
document.querySelector('#svg-container').innerHTML = svg;
})
.catch(err => {
console.error('Ошибка загрузки SVG:', err);
});
При ошибке важно не инициировать Vivus, поскольку отсутствие SVG
приведёт к исключениям при поиске <path>
элементов.
Не каждый SVG подходит для анимации без предварительной подготовки.
Vivus работает только с контурами (path, line,
polyline, circle и т.д.). Однако сложные SVG,
экспортированные из графических редакторов, часто содержат:
<g> группы без путей<mask> и <clipPath><image> растровые элементыПеред загрузкой через AJAX часто требуется оптимизация файла с помощью инструментов вроде SVGO, чтобы структура SVG была предсказуемой.
Если один и тот же контейнер используется для загрузки нескольких SVG подряд, необходимо уничтожать или переинициализировать предыдущую анимацию.
Vivus не всегда предоставляет явный метод destroy, поэтому применяется замена содержимого контейнера:
function replaceSVG(url, container) {
container.innerHTML = '';
return fetch(url)
.then(res => res.text())
.then(svg => {
container.innerHTML = svg;
const el = container.querySelector('svg');
return new Vivus(el, {
type: 'sync',
duration: 150
});
});
}
Такой подход предотвращает наложение анимаций и утечки DOM-ссылок.
При большом количестве SVG на странице возникает необходимость управлять очередностью загрузки. Параллельная загрузка может перегружать DOM и снижать производительность анимации.
Последовательная обработка:
async function loadMultiple(svgList, container) {
for (const url of svgList) {
const svgText = await fetch(url).then(r => r.text());
container.innerHTML = svgText;
const svg = container.querySelector('svg');
await new Promise(resolve => {
new Vivus(svg, {
duration: 120,
type: 'delayed'
}, resolve);
});
}
}
Такой подход обеспечивает завершение одной анимации перед началом следующей.
Повторная загрузка одинаковых файлов создаёт лишнюю нагрузку на сеть. Простая стратегия кэширования:
const svgCache = new Map();
function getSVG(url) {
if (svgCache.has(url)) {
return Promise.resolve(svgCache.get(url));
}
return fetch(url)
.then(res => res.text())
.then(svg => {
svgCache.set(url, svg);
return svg;
});
}
После этого вставка становится быстрее, а Vivus инициализируется без задержек, связанных с сетью.
При использовании AJAX-загрузки важно учитывать момент появления
контейнера в DOM. Если SVG загружается до рендера элемента,
querySelector вернёт null.
Корректная схема инициализации:
document.addEventListener('DOMContentLoaded', () => {
const container = document.getElementById('svg-container');
loadSVG('/assets/graphic.svg', container)
.then(svg => {
new Vivus(svg, {
type: 'scenario-sync',
duration: 180
});
});
});
При частой подгрузке SVG важно минимизировать перерасход ресурсов:
Наиболее затратной операцией остаётся вставка innerHTML,
поэтому при высоких нагрузках может использоваться
DocumentFragment или ручной парсинг XML:
const parser = new DOMParser();
const svgDoc = parser.parseFromString(svgText, 'image/svg+xml');
const svgNode = svgDoc.querySelector('svg');
container.appendChild(svgNode);
Такой метод уменьшает количество промежуточных преобразований строки в DOM.
Vivus позволяет гибко управлять анимацией загруженного SVG:
type: 'delayed' — последовательное рисование путейtype: 'sync' — одновременная анимацияtype: 'oneByOne' — поочерёдное прорисовывание с
контролем задержекПри динамической загрузке чаще всего используется
delayed, поскольку он лучше масштабируется на сложных
SVG:
new Vivus(svgElement, {
type: 'delayed',
duration: 200,
animTimingFunction: Vivus.EASE
});
После вставки SVG в DOM становится возможным его дополнительное модифицирование перед анимацией:
strokesvg.querySelectorAll('path').forEach(path => {
path.setAttribute('stroke', '#000');
path.setAttribute('fill', 'none');
});
Такая подготовка позволяет унифицировать внешний вид SVG перед запуском Vivus и избежать несоответствий между разными файлами.