Hover-эффекты

Взаимодействие анимации SVG с пользовательскими событиями строится вокруг управления экземпляром Vivus через стандартные DOM-события. Основная идея заключается в том, что анимация не запускается автоматически при загрузке страницы, а привязывается к состоянию наведения курсора на элемент.

Базовая структура работы строится вокруг создания экземпляра:

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

Ключевой момент — параметр start: 'manual', который отключает автоматический запуск и позволяет полностью контролировать момент начала отрисовки.


Привязка анимации к hover-событию

Наиболее распространённый сценарий — запуск анимации при наведении курсора и сброс при уходе.

const icon = new Vivus('icon-svg', {
  type: 'oneByOne',
  duration: 100,
  start: 'manual'
});

const element = document.getElementById('icon-svg');

element.addEventListener('mouseenter', () => {
  icon.reset().play();
});

element.addEventListener('mouseleave', () => {
  icon.stop().reset();
});

Комбинация методов обеспечивает предсказуемое поведение:

  • reset() возвращает SVG в исходное состояние
  • play() запускает анимацию заново
  • stop() прерывает текущее воспроизведение

Поведение при повторных наведениях

При частых входах и выходах курсора важно учитывать, что Vivus не кэширует прогресс как временное состояние UI. Каждый reset() полностью очищает текущую отрисовку.

Для более плавного UX применяется стратегия «возврата к последнему состоянию»:

element.addEventListener('mouseenter', () => {
  icon.stop();
  icon.play();
});

В этом случае не выполняется принудительный сброс, а текущая позиция анимации просто прерывается и продолжается заново.


Использование разных типов анимации в hover-сценариях

Поведение hover-эффектов сильно зависит от выбранного типа отрисовки.

delayed

Каждый путь SVG анимируется с небольшой задержкой:

new Vivus('icon', {
  type: 'delayed',
  duration: 140,
  start: 'manual'
});

Такой вариант создаёт эффект последовательного проявления линий при наведении.


oneByOne

Каждый контур отрисовывается по очереди:

new Vivus('icon', {
  type: 'oneByOne',
  duration: 90,
  start: 'manual'
});

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


sync

Все линии анимируются одновременно:

new Vivus('icon', {
  type: 'sync',
  duration: 80,
  start: 'manual'
});

Используется для быстрых hover-эффектов без визуальной задержки.


Управление состояниями через CSS-классы

Вместо прямой работы с событиями возможно использование CSS-классов как триггера логики:

const icon = new Vivus('icon-svg', {
  type: 'sync',
  duration: 100,
  start: 'manual'
});

const element = document.getElementById('icon-svg-container');

element.addEventListener('mouseenter', () => {
  element.classList.add('active');
  icon.reset().play();
});

element.addEventListener('mouseleave', () => {
  element.classList.remove('active');
  icon.stop().reset();
});

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


Hover-анимация нескольких SVG одновременно

При наличии группы иконок требуется отдельный экземпляр Vivus для каждого элемента:

document.querySelectorAll('.icon').forEach((el) => {
  const instance = new Vivus(el.id, {
    type: 'delayed',
    duration: 100,
    start: 'manual'
  });

  el.addEventListener('mouseenter', () => {
    instance.reset().play();
  });

  el.addEventListener('mouseleave', () => {
    instance.stop().reset();
  });
});

Основная особенность — отсутствие разделяемого состояния между экземплярами. Каждый SVG управляется независимо, что снижает риск конфликтов при параллельных hover-событиях.


Оптимизация частых hover-событий

При интенсивном движении курсора возможны множественные вызовы mouseenter и mouseleave. Для предотвращения избыточных перезапусков анимации применяется проверка состояния:

let isPlaying = false;

element.addEventListener('mouseenter', () => {
  if (isPlaying) return;
  isPlaying = true;

  icon.reset().play(() => {
    isPlaying = false;
  });
});

Callback в play() позволяет точно определить момент завершения анимации.


Обработка прерывания анимации

Метод stop() не сбрасывает прогресс, а лишь останавливает текущее воспроизведение. Это используется для создания эффекта «заморозки» при уходе курсора:

element.addEventListener('mouseleave', () => {
  icon.stop();
});

При повторном наведении анимация стартует заново или продолжается в зависимости от вызова reset().


Hover-эффекты с задержкой

Для уменьшения случайных срабатываний используется таймер:

let hoverTimer;

element.addEventListener('mouseenter', () => {
  hoverTimer = setTimeout(() => {
    icon.reset().play();
  }, 120);
});

element.addEventListener('mouseleave', () => {
  clearTimeout(hoverTimer);
  icon.stop().reset();
});

Такая схема предотвращает запуск анимации при кратковременном прохождении курсора над элементом.


Интеграция с делегированием событий

При большом количестве SVG-элементов применяется делегирование:

document.body.addEventListener('mouseenter', (e) => {
  if (!e.target.classList.contains('vivus-icon')) return;

  const id = e.target.id;
  const instance = vivusRegistry[id];

  instance.reset().play();
}, true);

Использование capture-фазы (true) позволяет перехватывать событие на этапе входа курсора.


Touch-события как альтернатива hover

На сенсорных устройствах hover отсутствует, поэтому используется touchstart:

element.addEventListener('touchstart', () => {
  icon.reset().play();
});

Дополнительно учитывается предотвращение двойного срабатывания:

element.addEventListener('touchstart', (e) => {
  e.preventDefault();
  icon.reset().play();
});

Синхронизация hover с внешними состояниями интерфейса

Hover-анимации часто зависят от состояния интерфейса, например активности кнопки или вкладки:

if (!button.disabled) {
  element.addEventListener('mouseenter', () => {
    icon.play();
  });
}

При изменении состояния UI анимация может быть деактивирована полностью:

function setDisabled(state) {
  if (state) {
    icon.stop().reset();
    element.classList.add('disabled');
  }
}

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

Для повторной активации без пересоздания экземпляра используется циклический сброс:

element.addEventListener('mouseleave', () => {
  icon.stop();
  icon.reset();
});

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