作品缩略图懒加载避免卡死,修复缓存文件清理bug

This commit is contained in:
2025-09-01 12:51:18 +08:00
parent ff05567e6b
commit a5f38a4eed
2 changed files with 190 additions and 29 deletions
+102 -4
View File
@@ -18,15 +18,25 @@
<!-- 多页作品缩略图 -->
<div v-if="artwork.page_count > 1" class="thumbnails">
<button v-for="(page, index) in artwork.meta_pages" :key="index" @click="$emit('pageChange', index)"
class="thumbnail" :class="{ active: currentPage === index }">
<img :src="getImageUrl(page.image_urls.square_medium)" :alt="`第 ${index + 1} 页`" crossorigin="anonymous" />
class="thumbnail" :class="{ active: currentPage === index }" ref="thumbnailRefs">
<!-- 懒加载的缩略图 -->
<img v-if="visibleThumbnails.has(index)"
:src="getImageUrl(page.image_urls.square_medium)"
:alt="`第 ${index + 1} 页`"
crossorigin="anonymous" />
<!-- 占位符 -->
<div v-else class="thumbnail-placeholder">
<div class="placeholder-content">
<span>{{ index + 1 }}</span>
</div>
</div>
</button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue';
import { ref, computed, watch, onMounted, onUnmounted } from 'vue';
import { getImageProxyUrl } from '@/services/api';
import type { Artwork } from '@/types';
import LoadingSpinner from '@/components/common/LoadingSpinner.vue';
@@ -49,6 +59,11 @@ const emit = defineEmits<{
const imageLoaded = ref(false);
const imageError = ref(false);
// 懒加载相关
const thumbnailRefs = ref<HTMLElement[]>([]);
const visibleThumbnails = ref<Set<number>>(new Set());
let intersectionObserver: IntersectionObserver | null = null;
// 计算当前图片URL
const currentImageUrl = computed(() => {
if (!props.artwork) return '';
@@ -65,16 +80,84 @@ const currentImageUrl = computed(() => {
// 使用统一的图片代理函数
const getImageUrl = getImageProxyUrl;
// 初始化懒加载
const initLazyLoading = () => {
// 清理之前的观察器
if (intersectionObserver) {
intersectionObserver.disconnect();
}
// 创建新的 Intersection Observer
intersectionObserver = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const index = parseInt(entry.target.getAttribute('data-index') || '0');
visibleThumbnails.value.add(index);
}
});
}, {
root: null,
rootMargin: '50px', // 提前50px开始加载
threshold: 0.1
});
// 观察所有缩略图
thumbnailRefs.value.forEach((el, index) => {
if (el) {
el.setAttribute('data-index', index.toString());
intersectionObserver?.observe(el);
}
});
};
// 监听页面变化,重置图片加载状态
watch(() => props.currentPage, () => {
imageLoaded.value = false;
imageError.value = false;
});
// 监听作品变化,重置图片加载状态
// 监听作品变化,重置图片加载状态和懒加载
watch(() => props.artwork.id, () => {
imageLoaded.value = false;
imageError.value = false;
// 重置懒加载状态
visibleThumbnails.value.clear();
// 重新初始化懒加载
setTimeout(() => {
initLazyLoading();
}, 100);
});
// 监听当前页面变化,确保当前页面的缩略图可见
watch(() => props.currentPage, (newPage) => {
// 确保当前页面的缩略图可见
visibleThumbnails.value.add(newPage);
});
// 监听缩略图引用变化
watch(thumbnailRefs, () => {
if (thumbnailRefs.value.length > 0) {
initLazyLoading();
}
}, { deep: true });
onMounted(() => {
// 确保当前页面的缩略图可见
visibleThumbnails.value.add(props.currentPage);
// 初始化懒加载
setTimeout(() => {
initLazyLoading();
}, 100);
});
onUnmounted(() => {
// 清理观察器
if (intersectionObserver) {
intersectionObserver.disconnect();
intersectionObserver = null;
}
});
</script>
@@ -171,6 +254,21 @@ watch(() => props.artwork.id, () => {
object-fit: cover;
}
.thumbnail-placeholder {
width: 100%;
height: 100%;
background: #f3f4f6;
display: flex;
align-items: center;
justify-content: center;
}
.placeholder-content {
color: #9ca3af;
font-size: 0.75rem;
font-weight: 500;
}
@media (max-width: 768px) {
.thumbnails {
padding: 0.5rem;