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

Архитектура взаимодействия Lottie Web и Vue

Lottie Web представляет собой runtime-библиотеку для воспроизведения анимаций, экспортированных из Adobe After Effects через Bodymovin. В контексте Vue.js она чаще всего используется как обёртка над DOM-элементом, в который происходит рендеринг SVG, Canvas или HTML5-анимации.

Ключевая особенность интеграции заключается в том, что Lottie управляет жизненным циклом анимации независимо от реактивной системы Vue, поэтому требуется явная синхронизация через lifecycle hooks и refs.

Основные задачи интеграции:

  • корректная инициализация анимации после монтирования DOM
  • управление жизненным циклом экземпляра анимации
  • реактивное изменение параметров (скорость, цикл, источник JSON)
  • освобождение ресурсов при уничтожении компонента

Установка зависимостей

Базовая библиотека устанавливается через npm:

npm install lottie-web

В Vue-проекте дополнительно может использоваться обёртка, однако большинство реализаций строятся напрямую поверх lottie-web для полного контроля.


Базовая интеграция через Composition API

Создание минимального компонента, который инициализирует Lottie-анимацию:

<template>
  <div ref="container" class="lottie-container"></div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import lottie from 'lottie-web'
import animationData from './animation.json'

const container = ref(null)
let animationInstance = null

onMounted(() => {
  animationInstance = lottie.loadAnimation({
    container: container.value,
    renderer: 'svg',
    loop: true,
    autoplay: true,
    animationData
  })
})

onBeforeUnmount(() => {
  if (animationInstance) {
    animationInstance.destroy()
  }
})
</script>

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

Lottie создаёт внутренний цикл рендеринга, который должен быть явно уничтожен при размонтировании компонента. В противном случае сохраняются слушатели и requestAnimationFrame-задачи.

Основные методы управления:

  • play() — запуск воспроизведения
  • pause() — пауза
  • stop() — остановка с возвратом к началу
  • destroy() — полное освобождение ресурсов
  • setSpeed(value) — изменение скорости воспроизведения

Реактивное управление параметрами

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

import { ref, watch } from 'vue'
import lottie from 'lottie-web'

const speed = ref(1)

watch(speed, (newSpeed) => {
  if (animationInstance) {
    animationInstance.setSpeed(newSpeed)
  }
})

Динамическая загрузка JSON-анимаций

В реальных приложениях JSON-анимации часто загружаются по сети. Это требует асинхронной инициализации.

onMounted(async () => {
  const response = await fetch('/animations/loading.json')
  const data = await response.json()

  animationInstance = lottie.loadAnimation({
    container: container.value,
    renderer: 'svg',
    loop: true,
    autoplay: true,
    animationData: data
  })
})

Интеграция через props (переиспользуемый компонент)

Компонентный подход предполагает передачу конфигурации через props:

<template>
  <div ref="container"></div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
import lottie from 'lottie-web'

const props = defineProps({
  src: Object,
  loop: Boolean,
  autoplay: Boolean,
  speed: Number
})

const container = ref(null)
let anim = null

onMounted(() => {
  anim = lottie.loadAnimation({
    container: container.value,
    renderer: 'svg',
    loop: props.loop,
    autoplay: props.autoplay,
    animationData: props.src
  })

  if (props.speed) {
    anim.setSpeed(props.speed)
  }
})

watch(() => props.speed, (v) => {
  if (anim) anim.setSpeed(v)
})

onBeforeUnmount(() => {
  anim?.destroy()
})
</script>

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

Lottie поддерживает воспроизведение отдельных частей анимации через segments.

anim.playSegments([0, 30], true)

Это используется для UI-индикаторов, переходов состояний и микроанимаций.


Обработка событий анимации

Lottie предоставляет систему событий, интегрируемую с Vue через подписку:

onMounted(() => {
  anim = lottie.loadAnimation({
    container: container.value,
    renderer: 'svg',
    loop: true,
    autoplay: false,
    animationData: props.src
  })

  anim.addEventListener('complete', () => {
    console.log('animation complete')
  })

  anim.addEventListener('loopComplete', () => {
    console.log('loop finished')
  })
})

Основные события:

  • complete
  • loopComplete
  • enterFrame
  • segmentStart

Интеграция с Intersection Observer

Оптимизация производительности достигается через запуск анимации только при появлении элемента в viewport.

let observer

onMounted(() => {
  observer = new IntersectionObserver(([entry]) => {
    if (entry.isIntersecting) {
      anim?.play()
    } else {
      anim?.pause()
    }
  })

  observer.observe(container.value)
})

onBeforeUnmount(() => {
  observer?.disconnect()
  anim?.destroy()
})

Lazy-loading анимаций

При большом количестве Lottie-элементов загрузка JSON может стать узким местом. Используется динамический импорт:

const loadAnimation = async () => {
  const module = await import('./animations/hero.json')

  anim = lottie.loadAnimation({
    container: container.value,
    renderer: 'svg',
    loop: true,
    autoplay: true,
    animationData: module.default
  })
}

Поддержка SSR (Nuxt.js)

В серверном рендеринге доступ к DOM отсутствует, поэтому инициализация должна происходить только на клиенте.

import { onMounted } from 'vue'

onMounted(() => {
  if (process.client) {
    lottie.loadAnimation({
      container: document.querySelector('#anim'),
      renderer: 'svg',
      loop: true,
      autoplay: true,
      path: '/animation.json'
    })
  }
})

Создание Vue-плагина для Lottie

Инкапсуляция логики через глобальный плагин:

import lottie from 'lottie-web'

export default {
  install(app) {
    app.config.globalProperties.$lottie = lottie
  }
}

Регистрация:

import { createApp } from 'vue'
import App from './App.vue'
import LottiePlugin from './plugins/lottie'

createApp(App)
  .use(LottiePlugin)
  .mount('#app')

TypeScript-интеграция

При использовании TypeScript добавляется типизация экземпляра:

import type { AnimationItem } from 'lottie-web'

let anim: AnimationItem | null = null

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

interface Props {
  src: object
  loop?: boolean
  autoplay?: boolean
  speed?: number
}

Управление несколькими анимациями

При работе с коллекциями Lottie-элементов используется массив ссылок:

const animations = []

onMounted(() => {
  document.querySelectorAll('.lottie').forEach((el) => {
    animations.push(
      lottie.loadAnimation({
        container: el,
        renderer: 'svg',
        loop: true,
        autoplay: true,
        path: el.dataset.src
      })
    )
  })
})

Оптимизация рендеринга SVG

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

  • отключение аутлайнов и теней в After Effects
  • упрощение path-структуры
  • ограничение количества одновременно активных слоёв
  • использование renderer: 'canvas' при высоких нагрузках

Контроль памяти и утечки

Основной источник утечек — незавершённые анимации и подписчики событий. Критически важно:

  • вызывать destroy()
  • очищать IntersectionObserver
  • удалять event listeners при необходимости
  • избегать повторной инициализации без уничтожения предыдущей

Интеграционные паттерны

Распространённые архитектурные подходы:

  • компонент-обёртка с props API
  • директива Vue (v-lottie)
  • сервисный слой для управления анимациями
  • глобальный стор для синхронизации состояний анимаций

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