Использование в React

Подключение и базовая интеграция Lottie Web в React чаще всего строится вокруг императивного управления анимацией через DOM-узел, поскольку библиотека работает напрямую с canvas или SVG и не является React-ориентированной по архитектуре.

Основная идея интеграции заключается в том, что React отвечает за жизненный цикл компонента, а Lottie Web — за управление анимацией внутри конкретного DOM-контейнера. Это требует аккуратного связывания useEffect, useRef и методов управления анимацией.


Lottie использует JSON-файлы, экспортированные из After Effects через Bodymovin. Этот JSON содержит описание слоёв, ключевых кадров, интерполяций и векторных данных.

Типичная структура подключения в React начинается с установки пакета:

npm install lottie-web

В большинстве случаев JSON импортируется напрямую или загружается по URL:

import animationData from './animation.json';

или

const animationPath = '/animations/loader.json';

Создание базового компонента

Связка React и Lottie Web строится через useRef, который указывает на контейнер, где будет рендериться анимация.

import { useEffect, useRef } from 'react';
import lottie from 'lottie-web';
import animationData from './animation.json';

const LottieAnimation = () => {
  const containerRef = useRef(null);

  useEffect(() => {
    const animation = lottie.loadAnimation({
      container: containerRef.current,
      renderer: 'svg',
      loop: true,
      autoplay: true,
      animationData
    });

    return () => animation.destroy();
  }, []);

  return <div ref={containerRef} />;
};

Ключевой момент — обязательное уничтожение анимации при размонтировании компонента, иначе возникают утечки памяти и дублирование canvas-слоёв.


Управление жизненным циклом анимации

Lottie Web предоставляет объект анимации, который позволяет управлять воспроизведением:

  • play()
  • pause()
  • stop()
  • setSpeed()
  • goToAndPlay()
  • goToAndStop()

В React это обычно связывается с useRef, чтобы сохранить экземпляр:

const animationRef = useRef(null);

useEffect(() => {
  animationRef.current = lottie.loadAnimation({
    container: containerRef.current,
    renderer: 'svg',
    loop: false,
    autoplay: false,
    animationData
  });

  return () => animationRef.current.destroy();
}, []);

Пример управления извне:

const play = () => {
  animationRef.current.play();
};

const pause = () => {
  animationRef.current.pause();
};

Реактивное управление через props

Часто требуется управлять состоянием анимации через свойства компонента: скорость, автозапуск, цикл.

const LottieAnimation = ({ speed = 1, isPlaying }) => {
  const containerRef = useRef(null);
  const animRef = useRef(null);

  useEffect(() => {
    animRef.current = lottie.loadAnimation({
      container: containerRef.current,
      renderer: 'svg',
      loop: true,
      autoplay: false,
      animationData
    });

    return () => animRef.current.destroy();
  }, []);

  useEffect(() => {
    if (!animRef.current) return;
    animRef.current.setSpeed(speed);
  }, [speed]);

  useEffect(() => {
    if (!animRef.current) return;
    isPlaying ? animRef.current.play() : animRef.current.pause();
  }, [isPlaying]);

  return <div ref={containerRef} />;
};

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


Выбор рендерера: SVG, Canvas, HTML

Lottie Web поддерживает несколько режимов рендеринга:

  • svg — наиболее распространённый вариант
  • canvas — лучше для сложных сцен и производительности
  • html — используется редко, в специфических случаях
lottie.loadAnimation({
  container: containerRef.current,
  renderer: 'canvas',
  loop: true,
  autoplay: true,
  animationData
});

SVG обеспечивает высокое качество и возможность стилизации через CSS, но может быть тяжелее при большом количестве элементов.

Canvas предпочтителен для больших анимаций с высокой частотой кадров.


Работа с динамической загрузкой JSON

В реальных приложениях анимации часто загружаются асинхронно:

useEffect(() => {
  let anim;

  fetch('/animations/data.json')
    .then(res => res.json())
    .then(data => {
      anim = lottie.loadAnimation({
        container: containerRef.current,
        renderer: 'svg',
        loop: true,
        autoplay: true,
        animationData: data
      });
    });

  return () => {
    if (anim) anim.destroy();
  };
}, []);

Этот подход важен при использовании CDN или CMS, где JSON обновляется независимо от сборки фронтенда.


Интеграция с Next.js и SSR

При использовании серверного рендеринга возникает проблема отсутствия window и DOM. Поэтому загрузка Lottie Web должна происходить только на клиенте.

import dynamic from 'next/dynamic';

const LottieAnimation = dynamic(() => import('./LottieAnimation'), {
  ssr: false
});

Дополнительно внутри компонента проверяется наличие DOM:

useEffect(() => {
  if (typeof window === 'undefined') return;

  const anim = lottie.loadAnimation({
    container: containerRef.current,
    renderer: 'svg',
    loop: true,
    autoplay: true,
    animationData
  });

  return () => anim.destroy();
}, []);

Управление сегментами анимации

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

animRef.current.playSegments([0, 50], true);

Сегменты часто используются для:

  • hover-анимаций
  • состояний загрузки
  • переходов между UI-статусами

Предзагрузка и оптимизация

Производительность напрямую зависит от размера JSON. В больших интерфейсах применяется предзагрузка:

const animationCache = {};

const loadAnimationData = async (url) => {
  if (animationCache[url]) return animationCache[url];

  const data = await fetch(url).then(r => r.json());
  animationCache[url] = data;

  return data;
};

Дополнительно используется:

  • уменьшение количества слоёв в After Effects
  • отключение выражений
  • оптимизация path-данных
  • использование gzip/brotli на сервере

Стилизация SVG-рендера

SVG позволяет влиять на элементы через CSS:

svg path {
  stroke: #4f46e5;
  stroke-width: 2px;
}

Однако структура SVG генерируется динамически, поэтому селекторы должны быть достаточно общими или применяться через обёртки.


Синхронизация с состоянием React

Lottie часто синхронизируется с UI-состоянием:

useEffect(() => {
  if (!animRef.current) return;

  if (status === 'loading') {
    animRef.current.play();
  } else if (status === 'done') {
    animRef.current.stop();
  }
}, [status]);

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


Обработка повторного монтирования

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

if (animRef.current) {
  animRef.current.destroy();
}

Также применяется useMemo для стабильности конфигурации:

const options = useMemo(() => ({
  renderer: 'svg',
  loop: true,
  autoplay: true,
  animationData
}), []);

Контроль производительности и частоты кадров

Для сложных интерфейсов используется ограничение FPS:

lottie.setQuality('low');

или динамическое переключение качества в зависимости от устройства.

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


Типизация и масштабируемость

В TypeScript проектах описывается тип анимации:

import { AnimationItem } from 'lottie-web';

const animRef = useRef<AnimationItem | null>(null);

Это позволяет безопасно управлять методами анимации и предотвращает обращения к undefined.


Паттерны переиспользования

Для масштабируемых систем создаются обёртки:

  • универсальный компонент Lottie
  • хук useLottie
  • сервис предзагрузки анимаций

Хук может выглядеть следующим образом:

const useLottie = (animationData) => {
  const containerRef = useRef(null);
  const animRef = useRef(null);

  useEffect(() => {
    animRef.current = lottie.loadAnimation({
      container: containerRef.current,
      renderer: 'svg',
      loop: true,
      autoplay: true,
      animationData
    });

    return () => animRef.current.destroy();
  }, [animationData]);

  return { containerRef, animRef };
};

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