mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-07-11 15:32:47 +08:00
perf: 优化导航栏动画流畅度
This commit is contained in:
@@ -4,6 +4,7 @@ import type { MediaInfo } from '@/api/types'
|
||||
import MediaCard from '@/components/cards/MediaCard.vue'
|
||||
import SlideView from '@/components/slide/SlideView.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useIntersectionObserver, until } from '@vueuse/core'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -12,6 +13,10 @@ const props = defineProps({
|
||||
apipath: String,
|
||||
linkurl: String,
|
||||
title: String,
|
||||
ready: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
})
|
||||
|
||||
// 提供给子组件的属性
|
||||
@@ -19,38 +24,71 @@ provide('rankingPropsKey', reactive({ ...props }))
|
||||
|
||||
// 组件加载完成
|
||||
const componentLoaded = ref(false)
|
||||
// 是否已尝试加载
|
||||
const hasTriedLoading = ref(false)
|
||||
|
||||
// 数据列表
|
||||
const dataList = ref<MediaInfo[]>([])
|
||||
|
||||
// 容器引用
|
||||
const containerRef = ref<HTMLElement | null>(null)
|
||||
|
||||
// 获取订阅列表数据
|
||||
async function fetchData() {
|
||||
try {
|
||||
if (!props.apipath) return
|
||||
dataList.value = await api.get(props.apipath)
|
||||
if (dataList.value.length > 0) componentLoaded.value = true
|
||||
if (dataList.value.length > 0) {
|
||||
// 数据获取后,等待 ready 信号再渲染,避免阻塞动画
|
||||
await until(() => props.ready).toBe(true)
|
||||
}
|
||||
componentLoaded.value = true
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
componentLoaded.value = true
|
||||
} finally {
|
||||
hasTriedLoading.value = true
|
||||
}
|
||||
}
|
||||
|
||||
// 加载时获取数据
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
// 使用 IntersectionObserver 实现懒加载
|
||||
const { stop } = useIntersectionObserver(
|
||||
containerRef,
|
||||
([{ isIntersecting }]) => {
|
||||
if (isIntersecting) {
|
||||
fetchData()
|
||||
stop()
|
||||
}
|
||||
},
|
||||
{
|
||||
rootMargin: '300px', // 提前加载距离
|
||||
},
|
||||
)
|
||||
|
||||
onActivated(() => {
|
||||
if (dataList.value.length == 0) {
|
||||
if (dataList.value.length == 0 && hasTriedLoading.value) {
|
||||
fetchData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SlideView v-if="componentLoaded">
|
||||
<template #content>
|
||||
<template v-for="data in dataList" :key="data.tmdb_id || data.douban_id || data.bangumi_id">
|
||||
<MediaCard :media="data" width="9rem" />
|
||||
<div ref="containerRef">
|
||||
<SlideView v-if="componentLoaded">
|
||||
<template #content>
|
||||
<template v-for="data in dataList" :key="data.tmdb_id || data.douban_id || data.bangumi_id">
|
||||
<MediaCard :media="data" width="9rem" />
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
</SlideView>
|
||||
</SlideView>
|
||||
<SlideView v-else-if="!componentLoaded">
|
||||
<template #content>
|
||||
<div v-for="i in 10" :key="i" style="width: 9rem">
|
||||
<VCard class="outline-none overflow-hidden">
|
||||
<div style="padding-bottom: 150%"></div>
|
||||
</VCard>
|
||||
</div>
|
||||
</template>
|
||||
</SlideView>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user