39 lines
2.0 KiB
TypeScript
39 lines
2.0 KiB
TypeScript
import type { DetailScreen } from './types'
|
|
const combiningMark = new RegExp('\\p{Mark}', 'u')
|
|
|
|
/** Conservative grapheme wrapping matches the fixed font and Fabric splitByGrapheme layout. */
|
|
export function estimateDetailTextHeight(text: string, width: number, fontSize: number, lineHeight: number): number {
|
|
if (!text) return Math.ceil(fontSize * 1.13)
|
|
let lines = 0
|
|
for (const paragraph of text.split('\n')) {
|
|
let occupied = 0; let paragraphLines = 1
|
|
for (const character of Array.from(paragraph)) {
|
|
if (combiningMark.test(character)) continue
|
|
const factor = /[MW@%]/.test(character) ? 1 : /[\x20-\x7e]/.test(character) ? 0.65 : 1.05
|
|
const next = fontSize * factor
|
|
if (occupied > 0 && occupied + next > width) { paragraphLines++; occupied = 0 }
|
|
occupied += next
|
|
}
|
|
lines += paragraphLines
|
|
}
|
|
return Math.ceil(lines * fontSize * 1.13 * lineHeight)
|
|
}
|
|
|
|
/** v1 preserves the template while letting copy grow without covering the product image. */
|
|
export function reflowDetailScreen(screen: DetailScreen): DetailScreen {
|
|
const [title, body] = screen.texts
|
|
if (!title || !body || !title.id.endsWith('-title') || !body.id.endsWith('-body')) return screen
|
|
const titleY = screen.logo ? Math.max(58, screen.logo.y + screen.logo.height + 24) : 58
|
|
const titleHeight = estimateDetailTextHeight(screen.title, title.width, title.fontSize, title.lineHeight)
|
|
const bodyY = titleY + titleHeight + 24
|
|
const bodyHeight = estimateDetailTextHeight(screen.body, body.width, body.fontSize, body.lineHeight)
|
|
const imageArea = { x: 0, y: bodyY + bodyHeight + 32, width: 750, height: 750 }
|
|
const height = Math.max(1200, imageArea.y + imageArea.height + 48)
|
|
if (height > 2400) throw new Error('文案过长,请精简后确认;每屏高度最多 2400px')
|
|
return {
|
|
...screen, height, imageArea,
|
|
...(screen.image ? { image: { assetId: screen.image.assetId, ...imageArea } } : {}),
|
|
texts: [{ ...title, text: screen.title, y: titleY }, { ...body, text: screen.body, y: bodyY }, ...screen.texts.slice(2)],
|
|
}
|
|
}
|