|
|
@@ -0,0 +1,134 @@
|
|
|
+<template>
|
|
|
+ <div :style="styleMap?.mainStyle" class="w-full h-full overflow-hidden relative box-border">
|
|
|
+ <div class="lottie-preview">
|
|
|
+ <DotLottieVue
|
|
|
+ v-if="animationData"
|
|
|
+ :key="playerKey"
|
|
|
+ class="lottie-preview__player"
|
|
|
+ autoplay
|
|
|
+ loop
|
|
|
+ :data="animationData"
|
|
|
+ :autoResizeCanvas="true"
|
|
|
+ :renderConfig="renderConfig"
|
|
|
+ :layout="layout"
|
|
|
+ />
|
|
|
+ <div v-else class="lottie-preview__empty">
|
|
|
+ <img :src="icon" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { computed, ref, watch } from 'vue'
|
|
|
+import { DotLottieVue } from '@lottiefiles/dotlottie-vue'
|
|
|
+import { useProjectStore } from '@/store/modules/project'
|
|
|
+import { useWidgetStyle } from '../hooks/useWidgetStyle'
|
|
|
+import { getFileByPath } from '@/utils'
|
|
|
+import icon from '../assets/icon/icon_39lottie.svg'
|
|
|
+
|
|
|
+const props = defineProps<{
|
|
|
+ width: number
|
|
|
+ height: number
|
|
|
+ lottie: string
|
|
|
+ styles: any
|
|
|
+ part?: string
|
|
|
+ state?: string
|
|
|
+}>()
|
|
|
+
|
|
|
+const projectStore = useProjectStore()
|
|
|
+const animationData = ref('')
|
|
|
+const layout = {
|
|
|
+ fit: 'fill' as const,
|
|
|
+ align: [0.5, 0.5] as [number, number]
|
|
|
+}
|
|
|
+
|
|
|
+const styleMap = useWidgetStyle({
|
|
|
+ widget: 'lv_lottie',
|
|
|
+ props
|
|
|
+})
|
|
|
+
|
|
|
+const lottieMeta = ref<{
|
|
|
+ width?: number
|
|
|
+ height?: number
|
|
|
+ frameRate?: number
|
|
|
+ frames?: number
|
|
|
+ valid: boolean
|
|
|
+}>({
|
|
|
+ valid: false
|
|
|
+})
|
|
|
+
|
|
|
+const resource = computed(() => {
|
|
|
+ return projectStore.project?.resources.others.find((item) => item.id === props.lottie)
|
|
|
+})
|
|
|
+
|
|
|
+const playerKey = computed(() => `${props.lottie}-${props.width}-${props.height}`)
|
|
|
+
|
|
|
+const renderConfig = computed(() => {
|
|
|
+ const dpr =
|
|
|
+ typeof window !== 'undefined' ? Math.min(4, Math.max(window.devicePixelRatio || 1, 2)) : 2
|
|
|
+
|
|
|
+ return {
|
|
|
+ devicePixelRatio: dpr
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+watch(
|
|
|
+ () => [props.lottie, projectStore.projectPath],
|
|
|
+ async () => {
|
|
|
+ lottieMeta.value = { valid: false }
|
|
|
+ animationData.value = ''
|
|
|
+
|
|
|
+ if (!props.lottie || !resource.value?.path || !projectStore.projectPath) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const content = await getFileByPath(projectStore.projectPath + resource.value.path, 'utf-8')
|
|
|
+ if (!content) return
|
|
|
+
|
|
|
+ animationData.value = content
|
|
|
+ const json = JSON.parse(content)
|
|
|
+ const frameStart = Number(json?.ip)
|
|
|
+ const frameEnd = Number(json?.op)
|
|
|
+ lottieMeta.value = {
|
|
|
+ width: Number(json?.w) || undefined,
|
|
|
+ height: Number(json?.h) || undefined,
|
|
|
+ frameRate: Number(json?.fr) || undefined,
|
|
|
+ frames:
|
|
|
+ Number.isFinite(frameStart) && Number.isFinite(frameEnd)
|
|
|
+ ? Math.max(0, Math.round(frameEnd - frameStart))
|
|
|
+ : undefined,
|
|
|
+ valid: true
|
|
|
+ }
|
|
|
+ } catch {
|
|
|
+ lottieMeta.value = { valid: false }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ immediate: true
|
|
|
+ }
|
|
|
+)
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.lottie-preview {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.lottie-preview__player,
|
|
|
+.lottie-preview__empty {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.lottie-preview__player {
|
|
|
+ display: block;
|
|
|
+}
|
|
|
+
|
|
|
+.lottie-preview__empty {
|
|
|
+ display: grid;
|
|
|
+ place-items: center;
|
|
|
+}
|
|
|
+</style>
|