Отслеживание начала анимации

Vivus запускает анимацию SVG как последовательное прорисовывание путей, и контроль момента старта анимации становится важной частью интеграции с интерфейсом, аналитикой и синхронизацией с другими эффектами. Внутри библиотеки нет отдельного «официального» события начала анимации в виде стандартного EventEmitter API, поэтому отслеживание старта реализуется через комбинацию встроенных хуков, обёрток методов и внешних механизмов наблюдения.

Анимация в Vivus инициируется тремя основными способами:

  • autostart: true — запуск сразу после инициализации
  • start: 'inViewport' — запуск при попадании SVG в область видимости
  • ручной вызов instance.play() или instance.reset().play()

Ключевой момент: фактический старт анимации происходит не в момент создания объекта, а в момент вызова внутреннего метода проигрывания. Это создаёт точку контроля, которую можно перехватить.

Перехват старта через обёртку метода play

Самый надёжный способ фиксировать начало — переопределение метода play у экземпляра Vivus.

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

const originalPlay = instance.play;

instance.play = function () {
  console.log('Анимация начала');

  if (typeof this.__onStart === 'function') {
    this.__onStart();
  }

  return originalPlay.apply(this, arguments);
};

instance.__onStart = () => {
  console.log('Хук старта выполнен');
};

Такой подход гарантирует фиксацию момента запуска вне зависимости от причины вызова play.

Универсальный хук старта через фабричную обёртку

При работе с множеством SVG удобнее централизовать контроль:

function createVivusWithStartHook(id, options, onStart) {
  const instance = new Vivus(id, options);

  const originalPlay = instance.play;

  instance.play = function () {
    onStart?.(instance);
    return originalPlay.apply(this, arguments);
  };

  return instance;
}

const svgAnim = createVivusWithStartHook(
  'logo',
  { type: 'sync', duration: 80 },
  () => console.log('SVG стартовал')
);

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

Использование состояния экземпляра для определения старта

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

Идея заключается в отслеживании перехода из состояния «ожидание» в «анимация»:

const instance = new Vivus('icon', { duration: 100 });

let started = false;

instance.play = (function (original) {
  return function () {
    if (!started) {
      started = true;
      console.log('Первый запуск анимации');
    }
    return original.apply(this, arguments);
  };
})(instance.play);

Этот метод полезен, когда важно отследить именно первый старт, а не последующие перезапуски.

Отслеживание старта при автозапуске

При autostart: true вызов play() может происходить внутри конструктора, поэтому простое переопределение после создания экземпляра иногда оказывается поздним. В таких случаях используется задержка и контроль через onReady.

const instance = new Vivus('logo', {
  type: 'delayed',
  duration: 100,
  onReady: function () {
    console.log('SVG готов, вот-вот начнётся анимация');
  }
});

onReady сигнализирует о завершении подготовки DOM и вычисления путей, но не гарантирует старт анимации. Поэтому его используют как подготовительный этап перед фактическим запуском.

Комбинированный подход: onReady + перехват play

Для точного контроля применяется связка двух механизмов:

const instance = new Vivus('logo', {
  duration: 120,
  onReady: function () {
    console.log('Готово к запуску');
  }
});

const originalPlay = instance.play;

instance.play = function () {
  console.log('Фактический старт анимации SVG');
  return originalPlay.apply(this, arguments);
};

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

Использование MutationObserver как внешнего детектора

Если доступ к экземпляру ограничен, можно наблюдать за изменениями атрибутов SVG. Vivus изменяет stroke-dashoffset для имитации прорисовки, что можно отследить:

const svg = document.getElementById('logo');

const observer = new MutationObserver(() => {
  console.log('Обнаружено изменение SVG — возможный старт анимации');
});

observer.observe(svg, {
  attributes: true,
  subtree: true,
  attributeFilter: ['style', 'stroke-dashoffset']
});

Этот метод менее точный, но полезен при интеграции с чужим кодом.

Синхронизация старта с UI-событиями

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

instance.play = function () {
  document.body.classList.add('svg-animation-running');

  requestAnimationFrame(() => {
    console.log('UI синхронизирован со стартом анимации');
  });

  return originalPlay.apply(this, arguments);
};

Использование requestAnimationFrame позволяет синхронизировать визуальные изменения с первым кадром анимации.

Централизованный трекинг старта для нескольких SVG

При работе с множеством экземпляров важно унифицировать механизм:

const vivusRegistry = [];

function registerVivus(id, options) {
  const instance = new Vivus(id, options);

  const originalPlay = instance.play;

  instance.play = function () {
    console.log(`Старт анимации для #${id}`);
    return originalPlay.apply(this, arguments);
  };

  vivusRegistry.push(instance);

  return instance;
}

Такой реестр позволяет контролировать все SVG-анимации на странице и строить глобальную систему событий.

Практическая модель событий поверх Vivus

Для превращения Vivus в событийно-ориентированную систему можно внедрить простой эмиттер:

function makeEventedVivus(id, options) {
  const events = {};

  const instance = new Vivus(id, options);

  instance.on = function (event, cb) {
    events[event] = cb;
  };

  const originalPlay = instance.play;

  instance.play = function () {
    events['start']?.();
    return originalPlay.apply(this, arguments);
  };

  return instance;
}

const v = makeEventedVivus('icon', { duration: 90 });

v.on('start', () => {
  console.log('SVG animation started');
});

Такой слой абстракции превращает библиотеку в управляемый анимационный движок с контролируемыми состояниями.