Анимация SVG в Vivus основана на последовательном прорисовывании
путей (<path>), где каждый элемент графики постепенно
«рисуется» по мере изменения свойства stroke-dashoffset.
При статической инициализации библиотека начинает анимацию сразу после
создания экземпляра, однако в сценариях с длинными лендингами и большим
количеством графики требуется отложенный запуск, привязанный к видимости
элемента в области просмотра.
Основной архитектурный паттерн в таких случаях строится вокруг режима
ручного старта (start: 'manual') и внешнего триггера,
который активирует метод воспроизведения анимации.
Базовая конфигурация экземпляра Vivus изменяется таким образом, чтобы анимация не стартовала автоматически:
const instance = new Vivus('svg-id', {
type: 'delayed',
duration: 120,
start: 'manual'
});
Ключевой параметр start: 'manual' переводит управление
жизненным циклом анимации во внешний код. Это позволяет инициировать
отрисовку строго в момент, когда SVG становится релевантным для
пользователя.
Метод запуска анимации:
instance.play();
При такой архитектуре Vivus превращается в управляемый анимационный движок, который может быть синхронизирован с любыми событиями интерфейса, включая прокрутку страницы.
Современный способ контроля появления элементов в области просмотра
основан на IntersectionObserver. Он заменяет ручную
обработку события scroll и обеспечивает более стабильную
производительность.
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const svg = entry.target;
const instance = svg.__vivusInstance;
if (instance) {
instance.play();
}
observer.unobserve(svg);
}
});
}, {
threshold: 0.2,
rootMargin: '0px 0px -10% 0px'
});
Привязка экземпляра Vivus к DOM-элементу выполняется через пользовательское свойство:
const svgElement = document.getElementById('svg-id');
svgElement.__vivusInstance = new Vivus(svgElement, {
type: 'delayed',
duration: 100,
start: 'manual'
});
observer.observe(svgElement);
Параметр threshold определяет долю видимости элемента,
при которой запускается анимация. Значение 0.2 означает
активацию при появлении 20% SVG в области просмотра.
rootMargin позволяет смещать границы срабатывания, создавая
эффект предзагрузки анимации до полного появления блока.
При наличии нескольких SVG-анимаций структура управления становится централизованной. Каждый элемент регистрируется в наблюдателе и получает собственный экземпляр Vivus.
document.querySelectorAll('[data-vivus]').forEach((svg) => {
const instance = new Vivus(svg, {
type: 'delayed',
duration: 80,
start: 'manual'
});
svg.__vivusInstance = instance;
observer.observe(svg);
});
Использование атрибута data-vivus формирует
декларативный подход, при котором логика анимации отделяется от
структуры разметки.
Для гибкости конфигурации применяется чтение параметров непосредственно из DOM:
<svg id="icon1" data-vivus data-duration="120" data-type="oneByOne"></svg>
document.querySelectorAll('[data-vivus]').forEach((svg) => {
const duration = parseInt(svg.dataset.duration || '100', 10);
const type = svg.dataset.type || 'delayed';
const instance = new Vivus(svg, {
type,
duration,
start: 'manual'
});
svg.__vivusInstance = instance;
observer.observe(svg);
});
Такой подход позволяет масштабировать систему анимаций без изменения JavaScript-кода при добавлении новых SVG-элементов.
После активации анимации наблюдение за элементом обычно прекращается.
Это снижает нагрузку на IntersectionObserver:
if (entry.isIntersecting) {
instance.play();
observer.unobserve(entry.target);
}
При сложных интерфейсах с динамической подгрузкой блоков может использоваться стратегия повторного наблюдения, если SVG перерисовывается или появляется в DOM повторно.
В окружениях без поддержки IntersectionObserver
применяется обработка события прокрутки. Такой метод требует
оптимизации, так как частые события scroll создают нагрузку на основной
поток.
function onScroll() {
const svgs = document.querySelectorAll('[data-vivus]');
svgs.forEach(svg => {
const rect = svg.getBoundingClientRect();
if (rect.top < window.innerHeight && rect.bottom > 0) {
const instance = svg.__vivusInstance;
if (instance && !svg.__animated) {
instance.play();
svg.__animated = true;
}
}
});
}
window.addEventListener('scroll', onScroll);
Для снижения количества вычислений применяется
requestAnimationFrame:
let ticking = false;
window.addEventListener('scroll', () => {
if (!ticking) {
ticking = true;
requestAnimationFrame(() => {
onScroll();
ticking = false;
});
}
});
Vivus поддерживает повторное воспроизведение, однако в большинстве случаев требуется явный сброс состояния SVG.
instance.reset().play();
Метод reset() возвращает начальное состояние путей,
восстанавливая исходные значения stroke-dashoffset. Это
позволяет повторно запускать анимацию при изменении условий
интерфейса.
При необходимости координации групп SVG-анимаций используется единый контроллер состояний. Каждый элемент получает метаданные о приоритете или задержке запуска:
svg.dataset.delay = "200";
if (entry.isIntersecting) {
const delay = parseInt(entry.target.dataset.delay || '0', 10);
setTimeout(() => {
entry.target.__vivusInstance.play();
}, delay);
}
Такая модель позволяет формировать каскадную анимацию, где элементы последовательно активируются при прокрутке.
В интерфейсах с динамической загрузкой контента SVG может появляться после первичного рендера страницы. В таких случаях наблюдатель инициализируется повторно или вызывается функция регистрации новых элементов:
function registerVivus(svg) {
const instance = new Vivus(svg, {
type: 'delayed',
duration: 90,
start: 'manual'
});
svg.__vivusInstance = instance;
observer.observe(svg);
}
При добавлении новых узлов через AJAX или SPA-механизмы функция регистрации вызывается для каждого нового SVG.
Дополнительная оптимизация достигается через скрытие SVG до момента активации:
svg[data-vivus] {
visibility: hidden;
}
svg[data-vivus].is-visible {
visibility: visible;
}
if (entry.isIntersecting) {
entry.target.classList.add('is-visible');
entry.target.__vivusInstance.play();
}
Это позволяет избежать частичного отображения незавершённой графики в момент первичной загрузки.
Vivus часто используется совместно с CSS-анимациями и библиотеками управления скроллом. В таких сценариях важно разделять зоны ответственности: Vivus управляет отрисовкой линий, тогда как внешние системы контролируют позиционирование, трансформации и переходы прозрачности.
Согласование таймингов достигается через единые события активации,
привязанные к IntersectionObserver, что обеспечивает
синхронный старт всех визуальных эффектов внутри одного блока
интерфейса.