Воспроизведение по hover

Взаимодействие через hover в контексте Lottie Web строится на управлении жизненным циклом анимации через API lottie-web и DOM-события мыши. Основная идея заключается в том, чтобы отключить автоматическое воспроизведение и полностью делегировать управление состоянием анимации событиям mouseenter, mouseleave, mouseover, mouseout.

Инициализация анимации выполняется с отключёнными автоматическими режимами:

const animation = lottie.loadAnimation({
  container: document.querySelector('.icon'),
  renderer: 'svg',
  loop: false,
  autoplay: false,
  path: '/animations/icon.json'
});

Ключевое значение имеют параметры:

  • autoplay: false — исключает автоматический старт
  • loop: false — предотвращает бесконечное воспроизведение
  • renderer: ‘svg’ — обеспечивает удобное управление и масштабируемость

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


Обработка событий наведения мыши

Поведение hover реализуется через стандартные DOM-события. Наиболее устойчивым вариантом является использование mouseenter и mouseleave, поскольку они не всплывают и не вызывают повторных срабатываний при переходе между дочерними элементами.

const container = document.querySelector('.icon');

container.addEventListener('mouseenter', () => {
  animation.goToAndPlay(0, true);
});

container.addEventListener('mouseleave', () => {
  animation.stop();
});

В этом сценарии:

  • при наведении анимация стартует с начала
  • при уходе курсора происходит остановка

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

container.addEventListener('mouseenter', () => {
  animation.play();
});

container.addEventListener('mouseleave', () => {
  animation.pause();
});

Такой подход сохраняет текущее состояние кадра и позволяет продолжить воспроизведение с точки остановки.


Управление сегментами для hover-анимации

Наиболее контролируемый способ реализации hover-эффектов в Lottie Web — использование сегментов анимации. Сегменты позволяют разделить композицию на логические части: вступление, активное состояние и возврат.

container.addEventListener('mouseenter', () => {
  animation.playSegments([0, 30], true);
});

container.addEventListener('mouseleave', () => {
  animation.playSegments([30, 60], true);
});

Сегментный подход обеспечивает:

  • точное управление фазами анимации
  • синхронизацию с визуальной логикой интерфейса
  • отсутствие необходимости вручную отслеживать текущий кадр

При использовании true во втором параметре происходит немедленный перезапуск сегмента.


Реализация эффекта “зацикленного hover”

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

container.addEventListener('mouseenter', () => {
  animation.loop = true;
  animation.play();
});

container.addEventListener('mouseleave', () => {
  animation.loop = false;
  animation.stop();
});

Альтернативный вариант — использование сегментов с бесконечным повторением последнего состояния:

container.addEventListener('mouseenter', () => {
  animation.playSegments([0, 45], true);
  animation.loop = true;
});

container.addEventListener('mouseleave', () => {
  animation.loop = false;
  animation.playSegments([45, 60], true);
});

Контроль направления воспроизведения

Hover-анимации часто используют эффект «вперёд-назад». Это реализуется через управление направлением проигрывания.

container.addEventListener('mouseenter', () => {
  animation.setDirection(1);
  animation.play();
});

container.addEventListener('mouseleave', () => {
  animation.setDirection(-1);
  animation.play();
});

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


Синхронизация состояния при быстрых переходах курсора

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

container.addEventListener('mouseenter', () => {
  animation.stop();
  animation.goToAndPlay(0, true);
});

container.addEventListener('mouseleave', () => {
  animation.stop();
});

Дополнительная стабилизация достигается блокировкой повторных запусков:

let isPlaying = false;

container.addEventListener('mouseenter', () => {
  if (isPlaying) return;
  isPlaying = true;
  animation.goToAndPlay(0, true);
});

animation.addEventListener('complete', () => {
  isPlaying = false;
});

Использование hover для частичного воспроизведения

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

container.addEventListener('mouseenter', () => {
  animation.playSegments([10, 25], true);
});

container.addEventListener('mouseleave', () => {
  animation.playSegments([25, 40], true);
});

Такая техника применяется для:

  • кнопок с визуальным откликом
  • иконок состояния
  • индикаторов загрузки

Работа с несколькими hover-анимациями на странице

При наличии множества элементов с Lottie-анимацией важно избегать глобального состояния. Каждая анимация должна быть независимой сущностью.

document.querySelectorAll('.lottie-hover').forEach((el) => {
  const anim = lottie.loadAnimation({
    container: el,
    renderer: 'svg',
    loop: false,
    autoplay: false,
    path: el.dataset.path
  });

  el.addEventListener('mouseenter', () => {
    anim.goToAndPlay(0, true);
  });

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

Такая структура обеспечивает:

  • изоляцию состояния
  • отсутствие конфликтов между экземплярами
  • масштабируемость интерфейса

Оптимизация производительности при hover-воспроизведении

Hover-анимации могут вызывать частые перерисовки, особенно при использовании SVG-рендерера. Для минимизации нагрузки применяются следующие подходы:

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

Пример предзагрузки:

const animationDataCache = {};

fetch('/animations/icon.json')
  .then(res => res.json())
  .then(data => {
    animationDataCache.icon = data;
  });

Интеграция hover с состояниями UI

Hover-воспроизведение часто связывается с логикой компонентов. Анимация становится визуальным отражением состояния интерфейса.

function setActiveState(el, anim) {
  el.classList.add('active');
  anim.setSpeed(1.2);
  anim.play();
}

function setInactiveState(el, anim) {
  el.classList.remove('active');
  anim.stop();
}

Использование скорости позволяет дополнительно дифференцировать состояния без изменения структуры JSON-анимации.


Комбинирование hover и каскадных эффектов

В сложных интерфейсах hover может запускать цепочку анимаций:

container.addEventListener('mouseenter', () => {
  animation.playSegments([0, 20], true);

  setTimeout(() => {
    animation.playSegments([20, 40], true);
  }, 200);
});

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


Управление завершением hover-анимации

Контроль финального состояния важен для предотвращения визуальных разрывов.

container.addEventListener('mouseleave', () => {
  animation.playSegments([40, 60], true);
});

animation.addEventListener('complete', () => {
  animation.goToAndStop(0, true);
});

Фиксация состояния после завершения позволяет избежать накопления промежуточных кадров и обеспечивает предсказуемое поведение при следующем наведении.