Lottie Web представляет собой runtime-библиотеку для воспроизведения анимаций, экспортированных из Adobe After Effects через Bodymovin. В контексте Vue.js она чаще всего используется как обёртка над DOM-элементом, в который происходит рендеринг SVG, Canvas или HTML5-анимации.
Ключевая особенность интеграции заключается в том, что Lottie управляет жизненным циклом анимации независимо от реактивной системы Vue, поэтому требуется явная синхронизация через lifecycle hooks и refs.
Основные задачи интеграции:
Базовая библиотека устанавливается через npm:
npm install lottie-web
В Vue-проекте дополнительно может использоваться обёртка, однако
большинство реализаций строятся напрямую поверх lottie-web
для полного контроля.
Создание минимального компонента, который инициализирует 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-анимации часто загружаются по сети. Это требует асинхронной инициализации.
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:
<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')
})
})
Основные события:
completeloopCompleteenterFramesegmentStartОптимизация производительности достигается через запуск анимации только при появлении элемента в 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()
})
При большом количестве 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
})
}
В серверном рендеринге доступ к DOM отсутствует, поэтому инициализация должна происходить только на клиенте.
import { onMounted } from 'vue'
onMounted(() => {
if (process.client) {
lottie.loadAnimation({
container: document.querySelector('#anim'),
renderer: 'svg',
loop: true,
autoplay: true,
path: '/animation.json'
})
}
})
Инкапсуляция логики через глобальный плагин:
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 добавляется типизация экземпляра:
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-рендерер является наиболее гибким, но может создавать нагрузку при сложных сценах. Для оптимизации применяются:
renderer: 'canvas' при высоких
нагрузкахОсновной источник утечек — незавершённые анимации и подписчики событий. Критически важно:
destroy()IntersectionObserverРаспространённые архитектурные подходы:
v-lottie)Каждый подход выбирается в зависимости от масштаба приложения и количества анимационных сущностей.