Загрузка SVG через Ajax

Анимация 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 в DOM и подготовка к анимации

После получения строки 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 для последующей анимации.


Инициализация Vivus после загрузки

После того как SVG становится частью DOM, можно запускать анимацию:

loadSVG('/assets/diagram.svg', document.getElementById('svg-container'))
  .then(svg => {
    new Vivus(svg, {
      type: 'delayed',
      duration: 200,
      start: 'autostart'
    });
  });

Здесь важно, что первым аргументом может быть как id элемента, так и сам DOM-узел. При динамической загрузке предпочтительнее передавать именно узел, так как идентификаторы часто отсутствуют или создаются на лету.


Работа с XMLHttpRequest

Несмотря на распространение 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.


Особенности CORS и загрузки внешних SVG

При попытке загрузить 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 к корректной анимации

Не каждый SVG подходит для анимации без предварительной подготовки. Vivus работает только с контурами (path, line, polyline, circle и т.д.). Однако сложные SVG, экспортированные из графических редакторов, часто содержат:

  • <g> группы без путей
  • <mask> и <clipPath>
  • <image> растровые элементы
  • трансформации, усложняющие расчёт

Перед загрузкой через AJAX часто требуется оптимизация файла с помощью инструментов вроде SVGO, чтобы структура SVG была предсказуемой.


Повторная инициализация при динамической подмене 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

При большом количестве 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);
    });
  }
}

Такой подход обеспечивает завершение одной анимации перед началом следующей.


Кэширование загруженных SVG

Повторная загрузка одинаковых файлов создаёт лишнюю нагрузку на сеть. Простая стратегия кэширования:

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 инициализируется без задержек, связанных с сетью.


Интеграция загрузки SVG с жизненным циклом страницы

При использовании 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 важно минимизировать перерасход ресурсов:

  • избегать повторного парсинга одинаковых SVG
  • уменьшать количество DOM-операций
  • использовать один контейнер вместо создания множества узлов
  • ограничивать одновременные анимации

Наиболее затратной операцией остаётся вставка innerHTML, поэтому при высоких нагрузках может использоваться DocumentFragment или ручной парсинг XML:

const parser = new DOMParser();
const svgDoc = parser.parseFromString(svgText, 'image/svg+xml');
const svgNode = svgDoc.querySelector('svg');
container.appendChild(svgNode);

Такой метод уменьшает количество промежуточных преобразований строки в DOM.


Объединение AJAX-загрузки и параметров Vivus

Vivus позволяет гибко управлять анимацией загруженного SVG:

  • type: 'delayed' — последовательное рисование путей
  • type: 'sync' — одновременная анимация
  • type: 'oneByOne' — поочерёдное прорисовывание с контролем задержек

При динамической загрузке чаще всего используется delayed, поскольку он лучше масштабируется на сложных SVG:

new Vivus(svgElement, {
  type: 'delayed',
  duration: 200,
  animTimingFunction: Vivus.EASE
});

Управление SVG после загрузки

После вставки SVG в DOM становится возможным его дополнительное модифицирование перед анимацией:

  • изменение цвета обводки через stroke
  • добавление классов для стилизации
  • удаление лишних групп
svg.querySelectorAll('path').forEach(path => {
  path.setAttribute('stroke', '#000');
  path.setAttribute('fill', 'none');
});

Такая подготовка позволяет унифицировать внешний вид SVG перед запуском Vivus и избежать несоответствий между разными файлами.