九宫格优化

This commit is contained in:
李琦
2026-08-01 16:01:05 +08:00
parent c3b4eeb4c4
commit a0db002ca6
3 changed files with 107 additions and 32 deletions

View File

@@ -38,18 +38,19 @@
</div>
</div>
<!-- 九宫格透明占位撑布局activate 后按行分批挂载真图减轻主线程尖峰 -->
<!-- 九宫格外层仅透明占位decode 完成后再挂边框+ 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">
<div
v-for="(img, imgIdx) in (page.images || [])"
:key="imgIdx"
class="aspect-square relative"
:class="[frameClass, imgIdx < nineShownCount ? '' : 'nine-grid-placeholder']"
class="aspect-square relative nine-grid-slot"
:class="{ 'nine-grid-placeholder': imgIdx >= nineShownCount }"
>
<div
v-if="imgIdx < nineShownCount"
v-reveal="getRevealConfig(imgIdx)"
class="absolute inset-0"
:class="frameClass"
>
<img
:src="img"
@@ -135,30 +136,34 @@ const yieldToMain = () => new Promise((resolve) => {
}
})
/** 离屏预解码,完成后再抬 shownCount避免先露白底框 */
const preloadBatch = (urls) => Promise.race([
Promise.allSettled(
urls.map((url) => {
if (!url) return Promise.resolve()
const img = new Image()
img.decoding = 'async'
img.src = url
return decodeImg(img)
})
),
new Promise((resolve) => setTimeout(resolve, BATCH_DECODE_TIMEOUT_MS)),
])
const runBatchedReveal = async () => {
const images = props.page.images || []
const total = images.length
if (!total) return
for (let start = 0; start < total; start += BATCH_SIZE) {
nineShownCount.value = Math.min(total, start + BATCH_SIZE)
const end = Math.min(total, start + BATCH_SIZE)
await preloadBatch(images.slice(start, end))
await yieldToMain()
// decode 完再挂载:白卡纸/边框与图片一同走 reveal
nineShownCount.value = end
await nextTick()
await yieldToMain()
const root = nineGridRef.value
const end = nineShownCount.value
const imgs = root
? [...root.querySelectorAll('img[data-nine-idx]')].filter((el) => {
const i = Number(el.getAttribute('data-nine-idx'))
return i >= start && i < end
})
: []
await Promise.race([
Promise.allSettled(imgs.map(decodeImg)),
new Promise((resolve) => setTimeout(resolve, BATCH_DECODE_TIMEOUT_MS)),
])
if (end < total) {
await new Promise((resolve) => setTimeout(resolve, BATCH_GAP_MS))
await yieldToMain()
@@ -259,13 +264,16 @@ function getRevealConfig(imgIdx) {
backface-visibility: hidden;
}
.nine-grid-img:hover { transform: translateZ(0) scale(1.03); }
/* 透明占位:占满格子尺寸但不露灰底,避免 activate 时布局跳动 */
.nine-grid-slot {
background: transparent;
}
/* 透明占位:占满格子尺寸,不露白底/边框 */
.nine-grid-placeholder {
visibility: hidden;
pointer-events: none;
background: transparent;
border: none;
box-shadow: none;
background: transparent !important;
border: none !important;
box-shadow: none !important;
}
.overlap-wrap { height: clamp(320px, 54vh, 460px); }