mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-05 21:06:38 +08:00
71 lines
2.3 KiB
TypeScript
71 lines
2.3 KiB
TypeScript
import { useEffect, useMemo, useState } from 'react'
|
|
|
|
import {
|
|
buildSpreadIndexOf,
|
|
buildSpreads,
|
|
hasImageSize,
|
|
loadImageSize,
|
|
type ComicSpread,
|
|
} from '../utils/comicSpread'
|
|
|
|
// 漫画双页铺开的分组 + 尺寸探测。
|
|
//
|
|
// 分组结果依赖每张图是否「横跨两页」,而这要等图片尺寸探测出来才能判断,
|
|
// 于是探测每完成一张就 bump 一次版本号、重新分组。为避免把整章图片都拉一遍,
|
|
// 只探测当前页附近的一小段窗口:向前看 8 张,翻到下一屏时它的尺寸已经就绪,
|
|
// 不会出现「先按竖版配好、渲染完再重排」的跳动。
|
|
|
|
/** 当前页往后探测几张。 */
|
|
const PROBE_AHEAD = 8
|
|
/** 当前页往前回看几张(滑块往回拖时补上断档)。 */
|
|
const PROBE_BEHIND = 1
|
|
|
|
export interface ComicSpreads {
|
|
/** 每屏要显示的图片序号。 */
|
|
spreads: ComicSpread[]
|
|
/** 图片序号 → 所在屏序号。 */
|
|
spreadIndexOf: number[]
|
|
}
|
|
|
|
/**
|
|
* @param srcs 整章图片地址。
|
|
* @param enabled 只在真的会用到双页时探测,单页/滚动模式不额外发请求。
|
|
* @param center 当前图片序号,决定探测窗口。
|
|
*/
|
|
export function useComicSpreads(srcs: string[], enabled: boolean, center: number): ComicSpreads {
|
|
const [revision, setRevision] = useState(0)
|
|
|
|
useEffect(() => {
|
|
if (!enabled || srcs.length === 0) return
|
|
let cancelled = false
|
|
const from = Math.max(0, center - PROBE_BEHIND)
|
|
const to = Math.min(srcs.length - 1, center + PROBE_AHEAD)
|
|
const targets: string[] = []
|
|
for (let i = from; i <= to; i += 1) {
|
|
const src = srcs[i]
|
|
if (src && !hasImageSize(src)) targets.push(src)
|
|
}
|
|
if (targets.length === 0) return
|
|
for (const src of targets) {
|
|
loadImageSize(src)
|
|
.then(() => {
|
|
if (!cancelled) setRevision((v) => v + 1)
|
|
})
|
|
.catch(() => undefined)
|
|
}
|
|
return () => {
|
|
cancelled = true
|
|
}
|
|
}, [srcs, enabled, center])
|
|
|
|
const spreads = useMemo(() => {
|
|
// revision 参与依赖:尺寸探测每完成一张就重新分组,宽图才能正确独占一屏
|
|
void revision
|
|
return buildSpreads(srcs)
|
|
}, [srcs, revision])
|
|
|
|
const spreadIndexOf = useMemo(() => buildSpreadIndexOf(spreads, srcs.length), [spreads, srcs.length])
|
|
|
|
return { spreads, spreadIndexOf }
|
|
}
|