恢复滚动方案

This commit is contained in:
李琦
2026-08-01 15:00:15 +08:00
parent f65000336a
commit 8fea25a5fc
2 changed files with 139 additions and 14 deletions

View File

@@ -38,13 +38,19 @@
</div>
</div>
<!-- 九宫格保留方向性 v-reveal 新动画 -->
<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)" />
</div>
<!-- 九宫格全入视口 activate 后再挂载/加载方向性 v-reveal -->
<div v-else-if="page.type === 'nine-grid'" ref="nineGridRef" class="nine-grid grid grid-cols-3 gap-1.5 px-4 mt-6">
<template v-if="activate">
<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)" />
</div>
</template>
<template v-else>
<div v-for="(_, imgIdx) in (page.images || [])" :key="`ph-${imgIdx}`"
class="aspect-square relative nine-grid-placeholder" :class="frameClass" aria-hidden="true" />
</template>
</div>
<!-- 瀑布流 -->
@@ -83,16 +89,57 @@
</template>
<script setup>
import { ref, computed } from 'vue'
import { ref, computed, watch, nextTick } from 'vue'
import { borderClass } from '@/composables/borderStyles'
const DECODE_TIMEOUT_MS = 4000
const props = defineProps({
page: { type: Object, required: true },
/** 九宫格专用:父级在 section 全入视口后设为 true 才加载 */
activate: { type: Boolean, default: false },
})
const emit = defineEmits(['preview-image'])
const emit = defineEmits(['preview-image', 'ready'])
const nineGridRef = ref(null)
let readyEmitted = false
const frameClass = computed(() => borderClass(props.page.borderStyle))
const waitImagesReady = async () => {
await nextTick()
const root = nineGridRef.value
if (!root) return
const imgs = [...root.querySelectorAll('img[src]')]
if (!imgs.length) return
const decodeAll = Promise.allSettled(
imgs.map((img) => {
if (typeof img.decode === 'function') return img.decode().catch(() => {})
if (img.complete) return Promise.resolve()
return new Promise((resolve) => {
img.addEventListener('load', resolve, { once: true })
img.addEventListener('error', resolve, { once: true })
})
})
)
await Promise.race([
decodeAll,
new Promise((resolve) => setTimeout(resolve, DECODE_TIMEOUT_MS)),
])
}
watch(
() => props.activate && props.page?.type === 'nine-grid',
async (on) => {
if (!on || readyEmitted) return
await waitImagesReady()
if (readyEmitted) return
readyEmitted = true
emit('ready')
},
{ flush: 'post' }
)
const singleWrapClass = computed(() =>
props.page.singleWidth === 'full' ? 'max-w-none' : 'max-w-[300px]'
)
@@ -174,6 +221,9 @@ function getRevealConfig(imgIdx) {
backface-visibility: hidden;
}
.nine-grid-img:hover { transform: translateZ(0) scale(1.03); }
.nine-grid-placeholder {
background: rgba(247, 246, 242, 0.85);
}
.overlap-wrap { height: clamp(320px, 54vh, 460px); }