九宫格优化
This commit is contained in:
@@ -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); }
|
||||
|
||||
Reference in New Issue
Block a user