更新页面效果,但是会卡顿

This commit is contained in:
李琦
2026-08-01 13:57:14 +08:00
parent bf8d6f5b0c
commit a6dec89d09
7 changed files with 340 additions and 74 deletions

View File

@@ -1,5 +1,5 @@
<template>
<div class="gallery">
<div ref="galleryRef" class="gallery">
<!-- 标题区 -->
<div v-reveal="'up'" class="text-center mb-5 px-4">
<span class="gallery-title calligraphy text-xl text-[#a88c6b] tracking-[0.3em] font-bold block mb-2">{{ page.title }}</span>
@@ -10,18 +10,18 @@
<!-- 单图默认居中卡幅 / 可配置宽度铺满 -->
<div v-if="page.type === 'single'" v-reveal="{ variant: 'scale', delay: 100 }"
class="w-full mx-auto mt-6" :class="singleWrapClass" :style="singleWrapStyle">
<div :class="singleFrameClass"><img :src="page.img1" class="preview-img" @click.stop="previewImage(page.img1)" @load="notifyLayoutChange" /></div>
<div :class="singleFrameClass"><img :src="page.img1" class="preview-img" decoding="async" @click.stop="previewImage(page.img1)" @load="notifyLayoutChange" /></div>
</div>
<!-- 错落双图交错拼贴左上大图 + 右下压角叠放微旋转clamp 封顶不溢出 -->
<div v-else-if="page.type === 'overlap'" class="overlap-wrap relative w-full mt-2">
<div v-reveal="{ variant: 'left', delay: 100 }"
class="absolute left-0 top-0 w-[72%] h-[78%] z-10 -rotate-2" :class="frameClass">
<img :src="page.img1" class="preview-img" @click.stop="previewImage(page.img1)" @load="notifyLayoutChange" />
<img :src="page.img1" class="preview-img" decoding="async" @click.stop="previewImage(page.img1)" @load="notifyLayoutChange" />
</div>
<div v-reveal="{ variant: 'right', delay: 300 }"
class="absolute right-0 bottom-0 w-[56%] h-[62%] z-20 rotate-3" :class="frameClass">
<img :src="page.img2" class="preview-img" @click.stop="previewImage(page.img2)" @load="notifyLayoutChange" />
<img :src="page.img2" class="preview-img" decoding="async" @click.stop="previewImage(page.img2)" @load="notifyLayoutChange" />
</div>
</div>
@@ -29,20 +29,19 @@
<div v-else-if="page.type === 'one-large-five-small'"
class="relative w-full aspect-square mt-6 grid grid-cols-3 grid-rows-3 gap-1.5 z-20">
<div v-reveal="{ variant: 'scale', delay: 100 }" class="col-span-2 row-span-2 relative" :class="frameClass">
<img :src="page.images[0]" class="preview-img hover:scale-105 transition-transform duration-1000" @click.stop="previewImage(page.images[0])" @load="notifyLayoutChange" />
<img :src="page.images[0]" class="preview-img hover:scale-105 transition-transform duration-1000" decoding="async" @click.stop="previewImage(page.images[0])" @load="notifyLayoutChange" />
</div>
<div v-for="i in [1,2,3,4,5]" :key="i" v-reveal="{ variant: 'up', delay: i * 80 }" class="relative" :class="frameClass">
<img :src="page.images[i]" class="preview-img" @click.stop="previewImage(page.images[i])" @load="notifyLayoutChange" />
<img :src="page.images[i]" class="preview-img" decoding="async" @click.stop="previewImage(page.images[i])" @load="notifyLayoutChange" />
</div>
</div>
<!-- 九宫格 -->
<!-- <div v-else-if="page.type === 'nine-grid'" v-reveal="{ variant: 'up', delay: 80 }" class="nine-grid grid grid-cols-3 gap-1.5 px-4 mt-6">-->
<div v-else-if="page.type === 'nine-grid'" class="nine-grid grid grid-cols-3 gap-1.5 px-4 mt-6">
<div v-for="(img, imgIdx) in page.images" :key="imgIdx"
v-reveal="getRevealConfig(imgIdx)"
class="aspect-square relative" :class="frameClass">
<img :src="img" class="nine-grid-img preview-img" @click.stop="previewImage(img)" @load="notifyLayoutChange" />
<img :src="img" class="nine-grid-img preview-img" decoding="async" @click.stop="previewImage(img)" @load="notifyLayoutChange" />
</div>
</div>
@@ -50,7 +49,7 @@
<div v-else-if="page.type === 'masonry'" class="g-masonry mt-6 px-1">
<div v-for="(img, imgIdx) in page.images" :key="imgIdx" v-reveal="{ variant: 'up', delay: imgIdx * 50 }"
class="g-masonry-item" :class="frameClass">
<img :src="img" class="preview-img" @click.stop="previewImage(img)" @load="notifyLayoutChange" />
<img :src="img" class="preview-img" decoding="async" @click.stop="previewImage(img)" @load="notifyLayoutChange" />
</div>
</div>
@@ -58,7 +57,7 @@
<div v-else-if="page.type === 'carousel'" class="relative mt-6">
<div ref="trackRef" class="g-carousel-track no-scrollbar" @scroll="onScroll">
<div v-for="(img, imgIdx) in page.images" :key="imgIdx" class="g-carousel-slide" :class="frameClass">
<img :src="img" class="preview-img" @click.stop="previewImage(img)" @load="notifyLayoutChange" />
<img :src="img" class="preview-img" decoding="async" @click.stop="previewImage(img)" @load="notifyLayoutChange" />
</div>
</div>
<button class="g-arrow g-prev" @click="goTo(index - 1)" aria-label="上一张"></button>
@@ -73,7 +72,7 @@
<div v-else-if="page.type === 'polaroid'" class="g-polaroid mt-6">
<div v-for="(img, imgIdx) in page.images" :key="imgIdx" v-reveal="{ variant: 'scale', delay: imgIdx * 70 }"
class="g-polaroid-item" :style="{ transform: polaroidTransform(imgIdx) }">
<img :src="img" class="preview-img" @click.stop="previewImage(img)" @load="notifyLayoutChange" />
<img :src="img" class="preview-img" decoding="async" @click.stop="previewImage(img)" @load="notifyLayoutChange" />
</div>
</div>
@@ -85,11 +84,17 @@
<script setup>
import { ref, computed } from 'vue'
import { borderClass } from '@/composables/borderStyles'
import { useImageWarmup } from '@/composables/useImageWarmup'
const props = defineProps({
page: { type: Object, required: true },
})
const emit = defineEmits(['preview-image', 'layout-change'])
const emit = defineEmits(['preview-image', 'layout-change', 'warmup-change'])
const galleryRef = ref(null)
useImageWarmup(galleryRef, {
onBusyChange: (busy) => emit('warmup-change', busy),
})
// 边框样式:前台按 page.borderStyle 套用全局 gf-* 类
const frameClass = computed(() => borderClass(props.page.borderStyle))
@@ -113,6 +118,13 @@ const singleFrameClass = computed(() =>
frameClass.value + (props.page.singleWidth === 'full' && props.page.singleKeepRatio ? ' gf-natural' : '')
)
/** 仅高度可能随图片加载变化的版式才通知父级重算 scroll metrics */
const needsLayoutNotify = computed(() => {
if (props.page.height === 'auto') return true
if (props.page.type === 'single' && props.page.singleWidth === 'full' && props.page.singleKeepRatio) return true
return false
})
/* ---------- 轮播状态 ---------- */
const index = ref(0)
const trackRef = ref(null)
@@ -136,6 +148,7 @@ function previewImage(src) {
}
function notifyLayoutChange() {
if (!needsLayoutNotify.value) return
emit('layout-change')
}
@@ -148,15 +161,14 @@ function polaroidTransform(i) {
return `rotate(${a}deg) translateY(${s}px)`
}
function getRevealConfig(imgIdx) {
const row = Math.floor(imgIdx / 3)
const col = imgIdx % 3
// 正中心
// 正中心:用 scale 代替 blur避免大图 filter 动画与解码叠峰
if (imgIdx === 4) {
return {
variant: 'blur',
variant: 'scale',
delay: 200,
}
}
@@ -172,7 +184,7 @@ function getRevealConfig(imgIdx) {
// 左右列
return {
variant: col === 0 ? 'left' : 'right',
delay: 80 + ((imgIdx +80) * 20) ,
delay: 80 + ((imgIdx + 80) * 20),
}
}
</script>
@@ -184,7 +196,6 @@ function getRevealConfig(imgIdx) {
font-family: var(--font-calligraphy);
font-weight: 700;
}
/*.gallery-subtitle { font-family: var(--font-display); }*/
.gallery-desc { font-family: var(--font-kai); }
.nine-grid-img {