vue3封装lottie-web组件,并读取zip文件播放动画

686 阅读3分钟

前言

  1. 业务接口返回用户信息,需要在页面展示用户的装扮道具,其中这些装扮道具有静态的资源和动态的资源。动态的资源都是用AE生成的带图片资源的Lottie动画。接口返回的用户装扮都是cdn链接,其中动态资源返回的是zip链接。
  2. 这里我使用的是lottie-web airbnb.io/lottie/#/ 这个库。通过阅读官方文档可以得知,lottie是通过json对象来启动动画的,由于接口是一返回一个zip链接,其中zip文件包含json配置,和图片资源。所以我这里只能读取zip里的json配置文件,配置animationData,来播放lottie动画。其实最好还是直接path引用json文件路径。

image.png 3.实现步骤:

  1. 获取 ZIP 文件: 使用浏览器的 Fetch API 或其他适当的网络请求库,从 CDN 获取 ZIP 文件。
  2. 解析 ZIP 文件: 使用 JSZip 或类似的库解析 ZIP 文件。
  3. 读取 Lottie JSON 文件: 一旦解析 ZIP 文件,读取其中的 Lottie JSON 文件。
  4. 更改 JSON 中的资源路径: 在 Lottie JSON 文件中,找到资源路径相关的字段,将其更新为正确的路径。通常,Lottie JSON 文件中的资源路径是相对路径,需要将其更新为指向 ZIP 文件中实际图片资源的路径。
  5. 将更新后的 JSON 传递给 Lottie: 将更新后的 JSON 配置传递给 Lottie 库,以便正确加载和显示动画。

封装lottie组件

<script setup lang="ts">
  import { ref, watch, onMounted, onUnmounted } from 'vue'
  import lottie, { AnimationItem } from 'lottie-web'
  const props = defineProps({
    speed: {
      type: Number,
      default: 1,
    },
    animationData: {
      type: Object,
      required: true,
    },
  })
  
  const animation = ref<AnimationItem | null>(null)
  const lottieContainer = ref<HTMLElement | null>(null)
  
  // 监听props.animationData变化
  watch(
    () => props.animationData,
    () => {
      loadAnimation()
    }
  )
  
  const loadAnimation = () => {
    if (animation.value) {
      animation.value.destroy()
    }
    animation.value = lottie.loadAnimation({
      container: lottieContainer.value as Element,
      renderer: 'svg',
      loop: true,
      autoplay: true,
      animationData: props.animationData,
    })
    animation.value.setSpeed(props.speed)
  }

  onMounted(() => {
    loadAnimation()
  })
  
  onUnmounted(() => {
    if (animation.value) {
      animation.value.stop() // 停止动画
      animation.value.destroy() // 销毁动画实例
    }
  })
</script>

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

<style scoped>
  /* 根据需要添加样式 */
</style>

通过cdn资源链接读取zip文件配置并修改json配置

// 引入jsZip解压zip文件
import JSZip from 'jszip'

// 读取 ZIP 文件
export async function fetchAndReadZip(url) {
  if (!url) {
    console.log('无效的url!')
    return
  }
  const zipFileUrl = url

  try {
    const response = await fetch(zipFileUrl)
    const zipBlob = await response.blob()
    const zip = await JSZip.loadAsync(zipBlob)

    // 存储所有图片的 Promise
    const imagePromises = []

    // 创建一个映射表来存储图片路径和对应的 Blob URL
    const blobUrlMap = {}

    // 遍历 ZIP 文件,处理图片
    for (const [relativePath, zipEntry] of Object.entries(zip.files)) {
      if (relativePath.startsWith('__MACOSX/')) {
        continue
      }

      if (zipEntry.name.match(/\.(jpg|jpeg|png)$/i)) {
        const blob = await zipEntry.async('blob')
        const blobUrl = URL.createObjectURL(blob)
        let start = zipEntry.name.lastIndexOf('/') + 1 // 找到最后一个 / 的位置,并从其后一位开始截取
        let result = zipEntry.name.substring(start) // 截取从 '/' 后到结尾的部分
        blobUrlMap[result] = blobUrl
        imagePromises.push(blob)
      }
    }

    // 遍历 ZIP 文件中的所有文件
    for (const [, zipEntry] of Object.entries(zip.files)) {
      const filePath = zipEntry.name

      // 根据文件路径中的后缀名判断是否为 JSON 文件
      if (filePath.endsWith('.json')) {
        let content = await zipEntry.async('string')
        content = JSON.parse(content)
        updateJsonPaths(content.assets, blobUrlMap)
        return content
      }
    }
  } catch (error) {
    console.error('Failed to fetch and read ZIP file:', error)
  }
}

// 更新 JSON 中的资源路径
function updateJsonPaths(jsonContent, blobUrlMap) {
  // 例如,将原始路径更新为 Blob URL
  jsonContent.forEach((item) => {
    if (item.p) {
      item.u = null
      item.p = blobUrlMap[item.p]
    }
  })
}

在组件中使用lottie组件

<script setup lang="ts">
import { fetchAndReadZip } from '@/utils/fetchZip'
// lottie 动画对象
const animationData: any = ref(null)
// 通过接口获取资源链接 https://xxxx.com/xxx.zip
const getActivityUserRankInfo = async () => {
    let res = await activityUserRankInfo({ activity_id: activityId })
    const { my_rank, rank_list } = res.data.data
    rankList.value = rank_list
    myRankInfo.value = my_rank
    if (my_rank.user_info.decoration.moving_head_pendant) {
      animationData.value = await fetchAndReadZip(my_rank.user_info.decoration.moving_head_pendant)
    }
  }
<script>

<template>
<LottieComponent
  v-if="animationData"
  :animationData="animationData"
  :speed="0.5"
/>
</template>

成功读取zip json文件修改资源路径,播放lottie动画

image.png lottie资源链接mqimg.meequ.cn/hobi_dtgj_1…