什么是 Lottie Lottie 是一个由 Airbnb 开发的库,它可以在 Web、iOS、Android 和 React Native 上渲染 Adobe After Effects 动画。简单来说,设计师可以在 After Effects 中创建精美的动画,然后通过 Bodymovin 插件导出为 JSON 格式,开发者再通过 Lottie 库将这些 JSON 文件渲染成高质量的动画。
这意味着你可以在你的网站或应用中使用复杂的动画,而不需要使用 GIF 或视频,从而获得更好的性能和质量。
Lottie动画示例
为什么使用 Lottie 在传统的 Web 开发中,如果我们想要在网页中展示动画,通常有以下几种方式:
GIF 图片:文件较大,质量较低,无法交互CSS 动画:适合简单动画,复杂动画难以实现JavaScript 动画库:如 GSAP,需要手动编写动画代码视频:文件大,加载慢,不够灵活而 Lottie 提供了一种全新的方式,它具有以下优势:
文件小:JSON 格式的动画文件通常比 GIF 或视频小得多无损缩放:基于矢量的动画可以无限缩放而不失真可交互:可以控制动画的播放、暂停、速度等性能好:使用 SVG 或 Canvas 渲染,性能优异设计开发协作:设计师可以直接输出动画,开发者直接使用基础知识准备 INFO
在开始使用 Lottie 之前,你需要了解一些基础知识:
1. 安装 lottie-web 首先,我们需要安装 lottie-web 库:
npmyarnpnpmbunsh# 使用 npm
npm install lottie-web12sh# 使用 yarn
yarn add lottie-web12sh# 使用 pnpm
pnpm add lottie-web12sh# 使用 bun
bun add lottie-web122. 获取 Lottie 动画文件 你可以从以下途径获取 Lottie 动画文件(JSON 格式):
设计师使用 After Effects 和 Bodymovin 插件导出使用在线资源,如 LottieFilesLottieFiles 网站截图
3. 了解 Vue3 的组合式 API 本教程将使用 Vue3 的组合式 API (Composition API) 来封装 Lottie 组件,如果你还不熟悉,可以先了解以下概念:
ref 和 reactive:用于创建响应式数据computed:计算属性watch:侦听器onMounted 和 onUnmounted:生命周期钩子defineProps 和 defineEmits:组件属性和事件最简单的使用方法 提示
在深入封装之前,让我们先看看最基础的使用方法,这有助于理解 Lottie 的工作原理。
1. 创建一个简单的 HTML 结构 HTMLhtml
12. 使用 JS 加载动画 JavaScriptjavascriptimport lottie from 'lottie-web';// 假设我们已经导入了动画数据
import animationData from './path/to/animation.json';
// 在组件挂载后
onMounted(() => {
const animation = lottie.loadAnimation({
container: document.getElementById('lottie-container'), // 动画容器
renderer: 'svg', // 渲染器类型:'svg' | 'canvas' | 'html'
loop: true, // 是否循环播放
autoplay: true, // 是否自动播放
animationData: animationData // 动画数据
});
// 可以通过 animation 对象控制动画
// animation.play(); // 播放
// animation.pause(); // 暂停
// animation.stop(); // 停止
});12345678910111213141516171819203. 在 Vue3 中的简单使用 在 Vue3 中,我们可以结合 ref 来引用 DOM 元素:
Lottie.vuevue
import lottie from 'lottie-web';
import { ref, onMounted, onUnmounted } from 'vue';
import animationData from './path/to/animation.json';
const lottieContainer = ref(null);
let animation = null;
onMounted(() => {
if (lottieContainer.value) {
animation = lottie.loadAnimation({
container: lottieContainer.value,
renderer: 'svg',
loop: true,
autoplay: true,
animationData: animationData
});
}
});
onUnmounted(() => {
// 组件卸载时销毁动画实例,避免内存泄漏
if (animation) {
animation.destroy();
}
});
12345678910111213141516171819202122232425262728293031NOTE
这是最基础的使用方法,但在实际项目中,我们通常需要更灵活的控制和更好的封装。接下来,我们将一步步学习如何封装一个功能完善的 Lottie 组件。
基础封装 提示
现在我们已经了解了 Lottie 的基本使用方法,接下来让我们开始封装一个简单的 Lottie 组件。封装的目的是让组件更易于复用,并且提供更好的接口供父组件调用。
1. 创建基础组件 首先,让我们创建一个名为 LottieAnimation.vue 的基础组件:
LottieAnimation.vuevue
import lottie from 'lottie-web';
import { ref, onMounted, onUnmounted } from 'vue';
// 定义组件属性
const props = defineProps({
animationData: {
type: Object,
required: true
}
});
const container = ref(null);
let animation = null;
onMounted(() => {
if (container.value && props.animationData) {
animation = lottie.loadAnimation({
container: container.value,
renderer: 'svg',
loop: true,
autoplay: true,
animationData: props.animationData
});
}
});
onUnmounted(() => {
if (animation) {
animation.destroy();
}
});
.lottie-container {
width: 100%;
height: 100%;
}
1234567891011121314151617181920212223242526272829303132333435363738394041424344这个组件非常简单,它只接收一个必需的 animationData 属性,并在组件挂载时创建动画,在组件卸载时销毁动画。
2. 使用基础组件 现在我们可以在其他组件中使用这个基础 Lottie 组件:
Lottie.vuevue
import LottieAnimation from './LottieAnimation.vue';
import animationData from './path/to/animation.json';
12345678910不做其他配置,默认为自动播放以及循环播放的SVG动画: 在这里插入图片描述
3. 添加基本配置选项 我们的基础组件还比较简单,让我们添加一些常用的配置选项,如循环播放、自动播放等:
LottieAnimation.vuevue
import lottie from 'lottie-web';
import { ref, onMounted, onUnmounted } from 'vue';
// 定义组件属性
const props = defineProps({
animationData: {
type: Object,
required: true
},
loop: { // 新增loop控制是否循环播放
type: Boolean,
default: true
},
autoplay: { // 新增autoplay控制是否自动播放//
type: Boolean,
default: true
},
width: {
type: [String, Number],
default: '100%'
},
height: {
type: [String, Number],
default: '100%'
}
});
const container = ref(null);
let animation = null;
onMounted(() => {
if (container.value && props.animationData) {
animation = lottie.loadAnimation({
container: container.value,
renderer: 'svg',
loop: props.loop, // 通过prop传入的loop//
autoplay: props.autoplay, // 通过prop传入的autoplay//
animationData: props.animationData
});
}
});
onUnmounted(() => {
if (animation) {
animation.destroy();
}
});
.lottie-container {
width: v-bind('props.width');
height: v-bind('props.height');
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960TIP
现在我们的组件可以接受更多的配置选项,使用起来更加灵活:
Lottie.vuevue// 父组件
:animation-data="animationData" :loop="false" :autoplay="true" width="300px" height="300px" /> 12345678910设置loop为false,动画播放完成便会停止: 在这里插入图片描述 4. 添加基本控制方法 添加基本控制方法 目前我们组件只能在初始化时设置一些配置,但无法在运行时控制动画。让我们添加一些基本的控制方法,如播放、暂停和停止: LottieAnimation.vuevue// LottieAnimation.vue
import lottie from 'lottie-web';
import { ref, onMounted, onUnmounted } from 'vue';
// 定义组件属性
const props = defineProps({
animationData: {
type: Object,
required: true
},
loop: {
type: Boolean,
default: true
},
autoplay: {
type: Boolean,
default: true
},
width: {
type: [String, Number],
default: '100%'
},
height: {
type: [String, Number],
default: '100%'
}
});
const container = ref(null);
let animation = null;
// 定义控制方法
const play = () => {
animation?.play(); // play-----从当前帧开始播放//
};
const pause = () => {
animation?.pause(); // pause-----暂停并保持当前帧//
};
const stop = () => {
animation?.stop(); // stop-----停止播放并重置到第0帧//
};
// 暴露方法给父组件
defineExpose({
play,
pause,
stop
});
onMounted(() => {
if (container.value && props.animationData) {
animation = lottie.loadAnimation({
container: container.value,
renderer: 'svg',
loop: props.loop,
autoplay: props.autoplay,
animationData: props.animationData
});
}
});
onUnmounted(() => {
if (animation) {
animation.destroy();
}
});
.lottie-container {
width: v-bind('props.width');
height: v-bind('props.height');
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081NOTE
现在父组件可以通过 ref 来调用这些方法:
Lottie.vuevue// 父组件
ref="lottieRef" :animation-data="animationData" :autoplay="false" />
import { ref } from 'vue';
import LottieAnimation from './LottieAnimation.vue';
import animationData from './path/to/animation.json';
const lottieRef = ref(null);
const playAnimation = () => {
lottieRef.value?.play();
};
const pauseAnimation = () => {
lottieRef.value?.pause();
};
const stopAnimation = () => {
lottieRef.value?.stop();
};
123456789101112131415161718192021222324252627282930313233设置自动播放为false,点击播放按钮从当前帧开始播放动画,暂停按钮会保持当前帧,再次点击播放继续从当前帧开始播放,停止按钮会终止动画重置当前帧为0在这里插入图片描述
进阶封装 进阶封装
现在我们已经有了一个基础的 Lottie 组件,但它还缺少一些高级功能,如事件处理、动画速度控制、方向控制等。接下来,我们将逐步添加这些功能。
1. 添加事件处理 Lottie 提供了多种事件,如动画完成、循环完成等。我们可以将这些事件暴露给父组件:
LottieAnimation.vuevue// LottieAnimation.vue
import lottie from 'lottie-web';
import { ref, onMounted, onUnmounted } from 'vue';
// 定义组件属性
const props = defineProps({
animationData: {
type: Object,
required: true
},
loop: {
type: Boolean,
default: true
},
autoplay: {
type: Boolean,
default: true
},
width: {
type: [String, Number],
default: '100%'
},
height: {
type: [String, Number],
default: '100%'
}
});
// 定义事件
const emit = defineEmits([
'complete',
'loopComplete',
'enterFrame',
'segmentStart',
'destroy'
]);
const container = ref(null);
let animation = null;
// 定义控制方法
const play = () => {
animation?.play();
};
const pause = () => {
animation?.pause();
};
const stop = () => {
animation?.stop();
};
// 暴露方法给父组件
defineExpose({
play,
pause,
stop
});
onMounted(() => {
if (container.value && props.animationData) {
animation = lottie.loadAnimation({
container: container.value,
renderer: 'svg',
loop: props.loop,
autoplay: props.autoplay,
animationData: props.animationData
});
// 注册事件监听
animation.addEventListener('complete', () => emit('complete'));
animation.addEventListener('loopComplete', () => emit('loopComplete'));
animation.addEventListener('enterFrame', (e) => emit('enterFrame', e));
animation.addEventListener('segmentStart', (e) => emit('segmentStart', e));
animation.addEventListener('destroy', () => emit('destroy'));
}
});
onUnmounted(() => {
if (animation) {
// 移除事件监听//
animation.removeEventListener('complete');
animation.removeEventListener('loopComplete');
animation.removeEventListener('enterFrame');
animation.removeEventListener('segmentStart');
animation.removeEventListener('destroy');
// 销毁动画实例//
animation.destroy();
animation = null;
}
});
.lottie-container {
width: v-bind('props.width');
height: v-bind('props.height');
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106现在父组件可以监听这些事件:
Lottie.vuevue// 父组件
:animation-data="animationData" @complete="onComplete" @loop-complete="onLoopComplete" /> import LottieAnimation from './LottieAnimation.vue'; import animationData from './path/to/animation.json'; const onComplete = () => { console.log('动画播放完成'); }; const onLoopComplete = () => { console.log('动画循环播放完成'); }; 1234567891011121314151617181920在这里插入图片描述 事件相关文档可翻到本文底部查看 API 参考 2. 添加速度和方向控制 添加速度和方向控制 Lottie 允许我们控制动画的播放速度和方向,让我们将这些功能添加到组件中: LottieAnimation.vuevue
import lottie from 'lottie-web';
import { ref, onMounted, onUnmounted, watch } from 'vue';
// 定义组件属性
const props = defineProps({
animationData: {
type: Object,
required: true
},
loop: {
type: Boolean,
default: true
},
autoplay: {
type: Boolean,
default: true
},
width: {
type: [String, Number],
default: '100%'
},
height: {
type: [String, Number],
default: '100%'
},
speed: { // 设置动画速度//
type: Number,
default: 1
},
direction: { // 设置动画方向//
type: Number,
default: 1,
validator: (value) => value === 1 || value === -1
}
});
// 定义事件
const emit = defineEmits([
'complete',
'loopComplete',
'enterFrame',
'segmentStart',
'destroy'
]);
const container = ref(null);
let animation = null;
// 定义控制方法
const play = () => {
animation?.play();
};
const pause = () => {
animation?.pause();
};
const stop = () => {
animation?.stop();
};
const setSpeed = (speed) => {
if (animation) {
animation.setSpeed(speed);
}
};
const setDirection = (direction) => {
if (animation) {
animation.setDirection(direction);
}
};
// 暴露方法给父组件
defineExpose({
play,
pause,
stop,
setSpeed,
setDirection
});
// 监听属性变化
watch(() => props.speed, (newSpeed) => {
if (animation) {
// 属性变化重新设置动画速度
animation.setSpeed(newSpeed);
}
});
watch(() => props.direction, (newDirection) => {
if (animation) {
// 属性变化重新设置动画方向
animation.setDirection(newDirection);
}
});
onMounted(() => {
if (container.value && props.animationData) {
animation = lottie.loadAnimation({
container: container.value,
renderer: 'svg',
loop: props.loop,
autoplay: props.autoplay,
animationData: props.animationData
});
// 设置初始速度和方向
animation.setSpeed(props.speed);
animation.setDirection(props.direction);
// 注册事件监听
animation.addEventListener('complete', () => emit('complete'));
animation.addEventListener('loopComplete', () => emit('loopComplete'));
animation.addEventListener('enterFrame', (e) => emit('enterFrame', e));
animation.addEventListener('segmentStart', (e) => emit('segmentStart', e));
animation.addEventListener('destroy', () => emit('destroy'));
}
});
onUnmounted(() => {
if (animation) {
// 移除事件监听
animation.removeEventListener('complete');
animation.removeEventListener('loopComplete');
animation.removeEventListener('enterFrame');
animation.removeEventListener('segmentStart');
animation.removeEventListener('destroy');
// 销毁动画实例
animation.destroy();
animation = null;
}
});
.lottie-container {
width: v-bind('props.width');
height: v-bind('props.height');
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147NOTE
现在通过父组件可以控制动画的速度和方向:
Lottie.vuevue// 父组件
ref="lottieRef" :animation-data="animationData" :speed="speed" :direction="direction" /> {{ speed }}x
import { ref } from 'vue';
import LottieAnimation from './LottieAnimation.vue';
import animationData from './path/to/animation.json';
const lottieRef = ref(null);
const speed = ref(1);
const direction = ref(1);
const toggleDirection = () => {
direction.value = direction.value === 1 ? -1 : 1;
};
12345678910111213141516171819202122232425262728293031323334在这里插入图片描述
3. 添加帧控制和分段播放 添加帧控制和分段播放
Lottie 还允许我们控制动画的帧和分段播放,这对于复杂动画的精确控制非常有用:
LottieAnimation.vuevue
import lottie from 'lottie-web';
import { ref, onMounted, onUnmounted, watch } from 'vue';
// 定义组件属性
const props = defineProps({
animationData: {
type: Object,
required: true
},
loop: {
type: Boolean,
default: true
},
autoplay: {
type: Boolean,
default: true
},
width: {
type: [String, Number],
default: '100%'
},
height: {
type: [String, Number],
default: '100%'
},
speed: {
type: Number,
default: 1
},
direction: {
type: Number,
default: 1,
validator: (value) => value === 1 || value === -1
},
segments: {
type: Array,
default: null
}
});
// 定义事件
const emit = defineEmits([
'complete',
'loopComplete',
'enterFrame',
'segmentStart',
'destroy'
]);
const container = ref(null);
let animation = null;
// 定义控制方法
const play = () => {
animation?.play();
};
const pause = () => {
animation?.pause();
};
const stop = () => {
animation?.stop();
};
const setSpeed = (speed) => {
if (animation) {
animation.setSpeed(speed);
}
};
const setDirection = (direction) => {
if (animation) {
animation.setDirection(direction);
}
};
const goToAndPlay = (value, isFrame = true) => {
if (animation) {
animation.goToAndPlay(value, isFrame);
}
};
const goToAndStop = (value, isFrame = true) => {
if (animation) {
animation.goToAndStop(value, isFrame);
}
};
const setSegment = (startFrame, endFrame) => {
if (animation) {
animation.setSegment(startFrame, endFrame);
}
};
const playSegments = (segments, forceFlag) => {
if (animation) {
animation.playSegments(segments, forceFlag);
}
};
// 暴露方法给父组件
defineExpose({
play,
pause,
stop,
setSpeed,
setDirection,
goToAndPlay,
goToAndStop,
setSegment,
playSegments
});
// 监听属性变化
watch(() => props.speed, (newSpeed) => {
if (animation) {
animation.setSpeed(newSpeed);
}
});
watch(() => props.direction, (newDirection) => {
if (animation) {
animation.setDirection(newDirection);
}
});
watch(() => props.segments, (newSegments) => {
if (animation && newSegments) {
animation.playSegments(newSegments, true);
}
}, { deep: true });
onMounted(() => {
if (container.value && props.animationData) {
animation = lottie.loadAnimation({
container: container.value,
renderer: 'svg',
loop: props.loop,
autoplay: props.autoplay,
animationData: props.animationData
});
// 设置初始速度和方向
animation.setSpeed(props.speed);
animation.setDirection(props.direction);
// 设置初始分段
if (props.segments) {
animation.playSegments(props.segments, true);
}
// 注册事件监听
animation.addEventListener('complete', () => emit('complete'));
animation.addEventListener('loopComplete', () => emit('loopComplete'));
animation.addEventListener('enterFrame', (e) => emit('enterFrame', e));
animation.addEventListener('segmentStart', (e) => emit('segmentStart', e));
animation.addEventListener('destroy', () => emit('destroy'));
}
});
onUnmounted(() => {
if (animation) {
// 移除事件监听
animation.removeEventListener('complete');
animation.removeEventListener('loopComplete');
animation.removeEventListener('enterFrame');
animation.removeEventListener('segmentStart');
animation.removeEventListener('destroy');
// 销毁动画实例
animation.destroy();
animation = null;
}
});
.lottie-container {
width: v-bind('props.width');
height: v-bind('props.height');
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188INFO
现在父组件可以更精确地控制动画:
Lottie.vuevue// 父组件
ref="lottieRef" :animation-data="animationData" />
import { ref } from 'vue';
import LottieAnimation from './LottieAnimation.vue';
import animationData from './path/to/animation.json';
const lottieRef = ref(null);
const goToFrame = (frame) => {
lottieRef.value?.goToAndPlay(frame, true);
};
const playFrames = (start, end) => {
lottieRef.value?.playSegments([start, end], true);
};
1234567891011121314151617181920212223242526272829在这里插入图片描述
INFO
到目前为止,我们已经封装了一个功能相对完善的 Lottie 组件,它可以满足大多数基本需求。在下一部分,我们将进一步完善这个组件,添加更多高级功能,并提供一个完整的 API 参考。
完整组件封装 完整组件封装
在前面的章节中,我们已经逐步构建了一个功能相对完善的 Lottie 组件。现在,让我们将所有功能整合起来,创建一个完整的 Lottie 组件,并添加一些额外的优化。
1. 添加渲染器选项 INFO
Lottie 支持多种渲染器,包括 SVG、Canvas 和 HTML。默认情况下,我们使用 SVG 渲染器,但在某些情况下,其他渲染器可能更适合。对于复杂动画,Canvas 渲染器会比 SVG 渲染器性能更好,让我们添加渲染器选项:
LottieAnimation.vuevue
import lottie from 'lottie-web';
import { ref, onMounted, onUnmounted, watch, computed } from 'vue';
// 定义组件属性
const props = defineProps({
animationData: {
type: Object,
required: true
},
loop: {
type: Boolean,
default: true
},
autoplay: {
type: Boolean,
default: true
},
width: {
type: [String, Number],
default: '100%'
},
height: {
type: [String, Number],
default: '100%'
},
speed: {
type: Number,
default: 1
},
direction: {
type: Number,
default: 1,
validator: (value) => value === 1 || value === -1
},
segments: {
type: Array,
default: null
},
// 通过父组件传入renderer决定渲染器
renderer: {
type: String,
default: 'svg',
validator: (value) => ['svg', 'canvas', 'html'].includes(value)
},
rendererSettings: {
type: Object,
default: () => ({})
},
initialSegment: {
type: Array,
default: null
}
});
// 计算样式
const containerStyle = computed(() => {
const width = typeof props.width === 'number' ? `${props.width}px` : props.width;
const height = typeof props.height === 'number' ? `${props.height}px` : props.height;
return {
width,
height,
overflow: 'hidden'
};
});
// 定义事件
const emit = defineEmits([
'complete',
'loopComplete',
'enterFrame',
'segmentStart',
'config_ready',
'data_ready',
'data_failed',
'loaded_images',
'DOMLoaded',
'destroy',
'error'
]);
const container = ref(null);
let animation = ref(null);
// 定义控制方法
const play = () => {
animation.value?.play();
};
const pause = () => {
animation.value?.pause();
};
const stop = () => {
animation.value?.stop();
};
const setSpeed = (speed) => {
if (animation.value) {
animation.value.setSpeed(speed);
}
};
const setDirection = (direction) => {
if (animation.value) {
animation.value.setDirection(direction);
}
};
const goToAndPlay = (value, isFrame = true) => {
if (animation.value) {
animation.value.goToAndPlay(value, isFrame);
}
};
const goToAndStop = (value, isFrame = true) => {
if (animation.value) {
animation.value.goToAndStop(value, isFrame);
}
};
const setSegment = (startFrame, endFrame) => {
if (animation.value) {
animation.value.setSegment(startFrame, endFrame);
}
};
const playSegments = (segments, forceFlag) => {
if (animation.value) {
animation.value.playSegments(segments, forceFlag);
}
};
const getDuration = (inFrames = true) => {
if (animation.value) {
return animation.value.getDuration(inFrames);
}
return 0;
};
// 暴露方法给父组件
defineExpose({
play,
pause,
stop,
setSpeed,
setDirection,
goToAndPlay,
goToAndStop,
setSegment,
playSegments,
getDuration,
lottieInstance: animation
});
// 初始化 Lottie 动画
const initLottie = () => {
if (!container.value || !props.animationData) return;
animation.value = lottie.loadAnimation({
container: container.value,
renderer: props.renderer,
loop: props.loop,
autoplay: props.autoplay,
animationData: props.animationData,
rendererSettings: props.rendererSettings,
initialSegment: props.initialSegment
});
// 设置初始速度和方向
animation.value.setSpeed(props.speed);
animation.value.setDirection(props.direction);
// 设置初始分段
if (props.segments) {
animation.value.playSegments(props.segments, true);
}
// 注册事件监听
animation.value.addEventListener('complete', () => emit('complete'));
animation.value.addEventListener('loopComplete', () => emit('loopComplete'));
animation.value.addEventListener('enterFrame', (e) => emit('enterFrame', e));
animation.value.addEventListener('segmentStart', (e) => emit('segmentStart', e));
animation.value.addEventListener('config_ready', () => emit('config_ready'));
animation.value.addEventListener('data_ready', () => emit('data_ready'));
animation.value.addEventListener('data_failed', (e) => emit('data_failed', e));
animation.value.addEventListener('loaded_images', () => emit('loaded_images'));
animation.value.addEventListener('DOMLoaded', () => emit('DOMLoaded'));
animation.value.addEventListener('destroy', () => emit('destroy'));
animation.value.addEventListener('error', (e) => emit('error', e));
};
// 监听属性变化
watch(() => props.speed, (newSpeed) => {
if (animation.value) {
animation.value.setSpeed(newSpeed);
}
}, { immediate: false });
watch(() => props.direction, (newDirection) => {
if (animation.value) {
animation.value.setDirection(newDirection);
}
}, { immediate: false });
watch(() => props.segments, (newSegments) => {
if (animation.value && newSegments) {
animation.value.playSegments(newSegments, true);
}
}, { immediate: false, deep: true });
// 监听动画数据变化,重新加载动画
watch(() => props.animationData, (newData) => {
if (animation.value) {
animation.value.destroy();
}
if (newData && container.value) {
initLottie();
}
}, { deep: true });
onMounted(() => {
initLottie();
});
onUnmounted(() => {
if (animation.value) {
// 移除所有事件监听
animation.value.removeEventListener('complete');
animation.value.removeEventListener('loopComplete');
animation.value.removeEventListener('enterFrame');
animation.value.removeEventListener('segmentStart');
animation.value.removeEventListener('config_ready');
animation.value.removeEventListener('data_ready');
animation.value.removeEventListener('data_failed');
animation.value.removeEventListener('loaded_images');
animation.value.removeEventListener('DOMLoaded');
animation.value.removeEventListener('destroy');
animation.value.removeEventListener('error');
// 销毁动画实例
animation.value.destroy();
animation.value = null;
}
});
.lottie-container {
display: inline-block;
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258至此,我们已经封装好了一个完整的,并且功能完善的Lottie组件。
3. 优化组件性能 为了进一步优化组件性能,我们可以考虑以下几点:
懒加载动画数据:对于较大的动画文件,可以考虑使用懒加载。使用 Canvas 渲染器:对于复杂动画,Canvas 渲染器可能比 SVG 渲染器性能更好。动态导入动画数据:使用 import() 动态导入动画数据。NOTE
下面是一个优化示例:
LottieAnimation.vuevue
import { ref, onMounted, onUnmounted, watch, computed } from 'vue';
// 定义组件属性
const props = defineProps({
//--------------------------------
// 与之前相同的属性......
src: {
type: String,
default: ''
}
});
//--------------------------------
// 与之前相同的代码......
// 动态加载动画数据
const loadAnimationData = async () => {
if (props.src) {
try {
const response = await fetch(props.src);
const data = await response.json();
if (animation.value) {
animation.value.destroy();
}
props.animationData = data;
initLottie();
} catch (error) {
console.error('Failed to load animation data:', error);
emit('error', error);
}
} else if (props.animationData) {
initLottie();
}
};
onMounted(() => {
if (props.src) {
loadAnimationData();
} else {
initLottie();
}
});
//--------------------------------
// 与之前相同的代码......
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051API 参考 下面是我们封装的 Lottie 组件的完整 API 参考。
属性 (Props) 属性名类型默认值说明animationDataObject必填Lottie 动画数据,JSON 格式loopBooleantrue是否循环播放动画autoplayBooleantrue是否自动播放动画widthString/Number'100%'动画容器宽度heightString/Number'100%'动画容器高度speedNumber1动画播放速度,1 表示正常速度directionNumber1动画播放方向,1 表示正向,-1 表示反向segmentsArraynull播放特定片段,格式为 [开始帧, 结束帧]initialSegmentArraynull初始播放片段,格式为 [开始帧, 结束帧]rendererString'svg'渲染器类型,可选值:'svg'、'canvas'、'html'rendererSettingsObject{}渲染器设置,详见 lottie-web 文档srcString''动画数据的 URL,如果提供,将从该 URL 加载动画数据事件 (Events) 事件名参数说明complete无动画播放完成时触发loopComplete无动画循环播放完成时触发enterFrame{ currentTime, totalTime, ... }进入新帧时触发segmentStart{ firstFrame, totalFrames, ... }片段开始播放时触发config_ready无配置就绪时触发data_ready无数据就绪时触发data_failederror数据加载失败时触发loaded_images无图像加载完成时触发DOMLoaded无DOM 加载完成时触发destroy无动画实例销毁时触发errorerror发生错误时触发方法 (Methods) 方法名参数返回值说明play无无播放动画pause无无暂停动画stop无无停止动画并回到第一帧setSpeedspeed: Number无设置动画播放速度,speed为1表示正常速度setDirectiondirection: Number无设置动画播放方向,1表示正向播放,-1表示反向播放goToAndPlayvalue: Number, isFrame: Boolean无跳转到指定帧或时间并播放,isFrame为true表示帧,为false表示时间,默认为falsegoToAndStopvalue: Number, isFrame: Boolean无跳转到指定帧或时间并停止,isFrame为true表示帧,为false表示时间,默认为falsesetSegmentstartFrame: Number, endFrame: Number无设置播放片段playSegmentssegments: Array, forceFlag: Boolean无播放指定片段,segments可以包含两个数字或者两个数字组成的数组,forceFlag表示是否立即强制播放该片段,例:anim.playSegments([[0,10],[30,45]], true) // 立即播放0-10帧和30-45帧 getDurationinFrames: Boolean = trueNumber获取动画持续时间,单位为帧或秒访问 Lottie 实例 如果你需要访问原始的 Lottie 实例来使用更多高级功能,可以通过 lottieInstance 属性获取:
javascriptconst lottieRef = ref(null);
// 在组件挂载后
onMounted(() => {
const lottieInstance = lottieRef.value?.lottieInstance.value;
if (lottieInstance) {
// 使用 lottieInstance 调用更多 lottie-web API
}
});123456789在本教程中,我们从零开始,一步步构建了一个功能完善的 Vue3 Lottie 组件。我们学习了:
Lottie 的基本概念和工作原理如何在 Vue3 中使用 lottie-web如何封装一个基础的 Lottie 组件如何添加事件处理、控制方法和配置选项如何优化组件性能组件的完整 API 和常见问题解决方案通过这个组件,你可以在 Vue3 项目中轻松地使用 Lottie 动画,为你的应用添加生动、高质量的动画效果。
希望这个教程对你有所帮助!