Запуск анимации при скролле

Анимация SVG в Vivus основана на последовательном прорисовывании путей (<path>), где каждый элемент графики постепенно «рисуется» по мере изменения свойства stroke-dashoffset. При статической инициализации библиотека начинает анимацию сразу после создания экземпляра, однако в сценариях с длинными лендингами и большим количеством графики требуется отложенный запуск, привязанный к видимости элемента в области просмотра.

Основной архитектурный паттерн в таких случаях строится вокруг режима ручного старта (start: 'manual') и внешнего триггера, который активирует метод воспроизведения анимации.

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

Базовая конфигурация экземпляра Vivus изменяется таким образом, чтобы анимация не стартовала автоматически:

const instance = new Vivus('svg-id', {
  type: 'delayed',
  duration: 120,
  start: 'manual'
});

Ключевой параметр start: 'manual' переводит управление жизненным циклом анимации во внешний код. Это позволяет инициировать отрисовку строго в момент, когда SVG становится релевантным для пользователя.

Метод запуска анимации:

instance.play();

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

Использование IntersectionObserver для отслеживания видимости

Современный способ контроля появления элементов в области просмотра основан на 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-элементов

При наличии нескольких 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 формирует декларативный подход, при котором логика анимации отделяется от структуры разметки.

Управление параметрами через data-атрибуты

Для гибкости конфигурации применяется чтение параметров непосредственно из 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 повторно.

Альтернативный подход через scroll-событие

В окружениях без поддержки 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);
}

Такая модель позволяет формировать каскадную анимацию, где элементы последовательно активируются при прокрутке.

Lazy-loading SVG и интеграция с динамическим контентом

В интерфейсах с динамической загрузкой контента SVG может появляться после первичного рендера страницы. В таких случаях наблюдатель инициализируется повторно или вызывается функция регистрации новых элементов:

function registerVivus(svg) {
  const instance = new Vivus(svg, {
    type: 'delayed',
    duration: 90,
    start: 'manual'
  });

  svg.__vivusInstance = instance;
  observer.observe(svg);
}

При добавлении новых узлов через AJAX или SPA-механизмы функция регистрации вызывается для каждого нового SVG.

Управление видимостью через CSS и предотвращение лишних отрисовок

Дополнительная оптимизация достигается через скрытие 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, что обеспечивает синхронный старт всех визуальных эффектов внутри одного блока интерфейса.