使用ts的话定义类型components/Lottie/type.ts
type Segment = [ number, number] export interface LottieEvent { play: () => void, // 播放动画 pause: () => void, // 暂停动画 stop: () => void, // 停止动画 setSpeed: (speed: number) => void, // 设置播放速度,1 表示1倍速度,0.5 表示 0.5倍速度 goToAndStop: (value: number, isFrame?: boolean) => void, // 跳到某一帧或某一秒停止,第二个参数 isFrame 为是否基于帧模式还是时间,默认为 false goToAndPlay: (value: number, isFrame?: boolean) => void, // 跳到某一帧或某一秒开始,第二个参数 isFrame 为是否基于帧模式还是时间,默认为 false setDirection: (direction: 1 | -1) => void, // 设置播放方向,1 表示正向播放,-1 表示反向播放 playSegments: (segments: Segment[], forceFlag?: boolean) => void, // 播放指定的片段,参数1为数组,两个元素为开始帧和结束帧;参数2为,是否立即播放片段,还是等之前的动画播放完成 destroy: () => void, // 销毁动画 getDuration: (isFrames: boolean) => void, // 获取动画时长,参数为是否基于帧模式,默认为 false. 如果为真,则返回以帧为单位的持续时间,如果为假,则以秒为单位。 [propname: string]: any } // Events // onComplete - 播放完成时触发 // onLoopComplete - 循环播放完成时触发 // onEnterFrame - 每一帧播放时触发 // onSegmentStart - 播放指定片段开始时触发
封装组件 components/Lottie/index.vue
<script setup>
import { ref, onMounted } from 'vue'
import lottie from 'lottie-web'
// 设置组件参数
const props = defineProps({
renderer: {
type: String,
default: 'svg',
},
// 循环播放
loop: {
type: Boolean,
default: true,
},
autoplay: {
type: Boolean,
default: true,
},
animationData: {
type: Object,
default: () => ({}),
},
name: {
type: String,
default: '',
}
})
// 创建 lottie 接收变量和获取dom
const animation = ref(null)
const dom = ref(null)
// 创建事件返回初始化lottie对象
const emits = defineEmits(['getAnimation', 'getDom'])
// 初始化渲染 lottie动画,并返回lottie对象
onMounted(() => {
animation.value = lottie.loadAnimation({
container: dom.value, // 用于渲染的容器
// 渲染方式 svg、canvas、html
renderer: props.renderer,
// 是否循环
loop: props.loop,
autoplay: props.autoplay, // 自动播放
// UED 提供的 动画的 json 文件
animationData: props.animationData,
name: props.name,
});
emits('getAnimation', animation.value)
})
</script>
<template>
<!-- 渲染 lottie 动画 -->
<div id="lottieId" ref="dom"></div>
</template>
<style scoped>
#lottieId {
width: 100%;
height: 100%;
}
</style>
引用组件,例如HelloWord.vue
<script setup>
import { ref, onMounted, watch } from 'vue'
import Lottie from './Lottie/index.vue'
import Animation from '../assets/Animation - 1712559820721.json'
import Animation1 from '../assets/Animation-1.json'
const a1 = ref(null)
const count = ref(0)
const likeFlag = ref(false)
const getAnimation = (animation) => {
a1.value = animation
if (a1.value) {
a1.value.addEventListener('enterFrame', () => {
// console.log(a1.value, '--a1.value--');
if (a1.value.currentFrame >= 63) { // 当前帧
a1.value.goToAndPlay(0, true) // 跳转到指定帧开始
}
})
a1.value.addEventListener('complete', () => {
console.log('播放完毕');
})
}
}
const play = () => {
if (likeFlag.value) {
a1.value.playSegments([31, 64], true) // 播放指定的片段,([开始帧,结束帧], 是否立即播放)
} else {
a1.value.playSegments([0, 30], true)
}
likeFlag.value = !likeFlag.value
count.value++
}
// const likeClick = () => {
// console.log('likeClick');
// a1.value.setSpeed(2) // 设置播放速度
// // a1.value.setDirection(-1) // 设置播放速度,不知道什么原因这里没有生效
// a1.value.stop()
// setTimeout(() => {
// // a1.value.goToAndStop(31, true) // 跳到指定帧结束,第二个参数 isFrame 为是否基于帧模式还是时间,默认为 false
// // a1.value.goToAndPlay(21, true) // 跳转到指定帧开始
// a1.value.play();
// }, 100); // 延迟100毫秒播放
// const duration = a1.value.getDuration(true) // true 为帧, false 为秒
// console.log(duration, '--duration--'); // 64帧 2.56秒
// }
// 监听鼠标滚动事件
let frame = 0
const maxFrame = 60; // 假设最大值为100
window.addEventListener('wheel', (event) => {
console.log(event, '--event--');
if (event.wheelDelta < 0) {
frame += 10
count.value++
} else if (event.wheelDelta >= 149) {
frame -= 10
count.value--
}
if (frame >= maxFrame) {
frame = 0; // 重置为0
} else if (frame < 0) {
frame = maxFrame; // 重置为最大值
}
a1.value.goToAndStop(frame, true)
console.log(frame, '--frame--');
})
onMounted(() => {
})
</script>
<template>
<div class="card">
<div>
<Lottie :animation-data="Animation" />
</div>
<div class="card-body">
<Lottie class="a1" :animation-data="Animation1" :loop="false" :autoplay="true" @get-animation="getAnimation"
@click="likeClick" />
<button @click="play">Play</button>
<div class="count">{{ count }}</div>
<div class="info">测试内容</div>
</div>
</div>
</template>
<style scoped>
.card {
display: flex;
align-items: center;
width: 700px;
/* margin: 0 auto; */
.card-body {
position: relative;
.info {
position: absolute;
left: 50%;
top: 20%;
transform: translate(-50%, -50%);
color: rgb(255, 255, 255);
}
}
}
</style>