mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-05 04:56:37 +08:00
优化
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import { comicImageSizing, isSelfSizedComicFit } from './comicImageFit.ts'
|
||||
import type { ComicImageFit } from '../stores/readerSettings'
|
||||
|
||||
// 漫画图片显示尺寸档位的纯逻辑回归测试(对齐 utils/coverUrl.test.ts 的写法)。
|
||||
|
||||
function check(name: string, condition: boolean) {
|
||||
if (!condition) throw new Error(`comicImageFit: ${name}`)
|
||||
}
|
||||
|
||||
// ── 是否由图片自身约束尺寸 ──
|
||||
const ALL_FITS: ComicImageFit[] = ['default', 'width', 'height', 'long', 'original']
|
||||
check('default 不是自尺寸档位', !isSelfSizedComicFit('default'))
|
||||
check('width 不是自尺寸档位', !isSelfSizedComicFit('width'))
|
||||
check('height 是自尺寸档位', isSelfSizedComicFit('height'))
|
||||
check('long 是自尺寸档位', isSelfSizedComicFit('long'))
|
||||
check('original 是自尺寸档位', isSelfSizedComicFit('original'))
|
||||
check(
|
||||
'自尺寸档位与列宽档位互斥且覆盖全部取值',
|
||||
ALL_FITS.every((fit) => isSelfSizedComicFit(fit) === (fit === 'height' || fit === 'long' || fit === 'original')),
|
||||
)
|
||||
|
||||
// ── default / width:宽度交给外层列宽,图片只负责铺满 ──
|
||||
for (const fit of ['default', 'width'] as ComicImageFit[]) {
|
||||
const loaded = comicImageSizing(fit, 800, true)
|
||||
check(`${fit} 加载完成时不留占位高度`, loaded.style.minHeight === undefined)
|
||||
check(`${fit} 铺满外层列宽`, loaded.className === 'block w-full')
|
||||
check(`${fit} 不自己定高`, loaded.style.height === undefined && loaded.style.maxHeight === undefined)
|
||||
check(`${fit} 不改 maxWidth`, loaded.style.maxWidth === undefined)
|
||||
check(`${fit} 加载中给占位高度`, comicImageSizing(fit, 800, false).style.minHeight === '10rem')
|
||||
}
|
||||
|
||||
// ── height:一屏一页 ──
|
||||
check('height 用滚动容器的实测高度', comicImageSizing('height', 592, true).style.height === '592px')
|
||||
check('height 宽度按原始比例', comicImageSizing('height', 592, true).style.width === 'auto')
|
||||
check('height 防超宽页横向溢出', comicImageSizing('height', 592, true).style.maxWidth === '100vw')
|
||||
check('height 不额外设 maxHeight', comicImageSizing('height', 592, true).style.maxHeight === undefined)
|
||||
check('height 类名不带 w-full(w-full 会顶掉高度约束)', comicImageSizing('height', 592, true).className === 'block')
|
||||
|
||||
// ── long:整页完整可见 ──
|
||||
check('long 用容器高度封顶', comicImageSizing('long', 592, true).style.maxHeight === '592px')
|
||||
check('long 不写死高度', comicImageSizing('long', 592, true).style.height === undefined)
|
||||
check('long 同时约束宽度', comicImageSizing('long', 592, true).style.maxWidth === '100vw')
|
||||
check('long 加载中给占位高度', comicImageSizing('long', 592, false).style.minHeight === '10rem')
|
||||
|
||||
// ── original:原始像素 1:1 ──
|
||||
check('original 不限制宽度', comicImageSizing('original', 592, true).style.maxWidth === 'none')
|
||||
check('original 不限制高度', comicImageSizing('original', 592, true).style.maxHeight === undefined)
|
||||
check('original 按原始像素渲染', comicImageSizing('original', 592, true).style.width === 'auto')
|
||||
|
||||
// ── 视口高度还没量出来时退回视口单位,首帧不塌 ──
|
||||
for (const fit of ['height', 'long'] as ComicImageFit[]) {
|
||||
check(`${fit} 未量到高度时退回 100dvh`, comicImageSizing(fit, 0, true).style[fit === 'height' ? 'height' : 'maxHeight'] === '100dvh')
|
||||
check(`${fit} 高度为负同样退回 100dvh`, comicImageSizing(fit, -10, true).style[fit === 'height' ? 'height' : 'maxHeight'] === '100dvh')
|
||||
}
|
||||
check('default 与视口高度无关', comicImageSizing('default', 0, true).style.height === undefined)
|
||||
@@ -0,0 +1,59 @@
|
||||
import type { CSSProperties } from 'react'
|
||||
|
||||
import type { ComicImageFit } from '../stores/readerSettings'
|
||||
|
||||
// 漫画图片的显示尺寸:把「档位」翻译成 <img> 的 class/style(纯逻辑,便于单测)。
|
||||
//
|
||||
// 只有上下滚动模式用得到:翻页模式是整页缩放进视口(object-contain),本来就没有
|
||||
// 「太大/太小」的问题,一个档位反而会和双页铺开的排版打架。
|
||||
//
|
||||
// 两种控制方式:
|
||||
// - default / width 由外层列宽决定(列封顶 900px 或铺满窗口),图片本身 w-full;
|
||||
// - height / long / original 由图片自身约束,所以外层必须放开列宽上限
|
||||
// (见 ReaderViewPage 的 comicFullWidth),否则 900px 的列会把它们再压回去。
|
||||
|
||||
/** 图片还没加载出来时的占位高度,避免懒加载期间整屏塌成 0 高。 */
|
||||
const PLACEHOLDER_MIN_HEIGHT = '10rem'
|
||||
|
||||
export interface ComicImageSizing {
|
||||
className: string
|
||||
style: CSSProperties
|
||||
}
|
||||
|
||||
/** 该档位是否由 <img> 自身约束尺寸(决定外层列宽要不要放开)。 */
|
||||
export function isSelfSizedComicFit(fit: ComicImageFit): boolean {
|
||||
return fit === 'height' || fit === 'long' || fit === 'original'
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动模式下漫画图片的尺寸样式。
|
||||
*
|
||||
* @param fit 显示尺寸档位。
|
||||
* @param viewportHeight 滚动容器的高度(px)。height/long 按它换算;还没量出来
|
||||
* (0)时退回 `100dvh`,首帧不会因为 0 而塌掉。
|
||||
* @param loaded 图片是否已加载完成,未完成时给个占位高度。
|
||||
*/
|
||||
export function comicImageSizing(
|
||||
fit: ComicImageFit,
|
||||
viewportHeight: number,
|
||||
loaded: boolean,
|
||||
): ComicImageSizing {
|
||||
const vh = viewportHeight > 0 ? `${viewportHeight}px` : '100dvh'
|
||||
// height 档位自带高度,占位高度没有意义也不冲突,统一先铺再被档位覆盖。
|
||||
const placeholder: CSSProperties = loaded ? {} : { minHeight: PLACEHOLDER_MIN_HEIGHT }
|
||||
|
||||
switch (fit) {
|
||||
case 'height':
|
||||
// 一屏一页:高度贴合视口,宽度按原始比例(maxWidth 防止超宽页横向溢出)
|
||||
return { className: 'block', style: { ...placeholder, height: vh, width: 'auto', maxWidth: '100vw' } }
|
||||
case 'long':
|
||||
// 整页完整可见:长边贴合视口,宽高都不超出
|
||||
return { className: 'block', style: { ...placeholder, maxHeight: vh, maxWidth: '100vw', width: 'auto' } }
|
||||
case 'original':
|
||||
// 原始像素 1:1:不放大也不缩小;比窗口宽时由滚动容器横向滚动
|
||||
return { className: 'block', style: { ...placeholder, width: 'auto', maxWidth: 'none' } }
|
||||
default:
|
||||
// default / width:宽度交给外层列(900px 封顶或铺满窗口),这里只管铺满
|
||||
return { className: 'block w-full', style: placeholder }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user