1. 礼物特效

This commit is contained in:
李琦
2026-08-03 13:19:09 +08:00
parent abcb53b88e
commit 8a4b3f4ab7
8 changed files with 532 additions and 173 deletions

View File

@@ -18,6 +18,75 @@ export const MOBILE_LAYOUTS = [
{ value: 'landscape', label: '横屏展示', desc: '手机端强制左右分栏' },
]
/** 后台 9×9 快速选点边长 */
export const HERO_FOCUS_GRID = 9
/** 旧版命名焦点 → 百分比(兼容已保存配置) */
const LEGACY_HERO_FOCUS = {
'top-left': [0, 0],
top: [50, 0],
'top-right': [100, 0],
left: [0, 50],
center: [50, 50],
right: [100, 50],
'bottom-left': [0, 100],
bottom: [50, 100],
'bottom-right': [100, 100],
}
export function clampHeroFocus(n, fallback = 50) {
const v = Number(n)
if (!Number.isFinite(v)) return fallback
return Math.min(100, Math.max(0, Math.round(v * 10) / 10))
}
/** 生成 9×9 网格点 {x,y,key} */
export function heroFocusGridPoints(size = HERO_FOCUS_GRID) {
const n = Math.max(2, size | 0)
const pts = []
for (let row = 0; row < n; row += 1) {
for (let col = 0; col < n; col += 1) {
const x = (col / (n - 1)) * 100
const y = (row / (n - 1)) * 100
pts.push({
key: `${row}-${col}`,
x: clampHeroFocus(x),
y: clampHeroFocus(y),
})
}
}
return pts
}
/**
* object-position CSS
* - heroFocusCss(cfg) 读 cfg.heroFocusX/Y
* - heroFocusCss(x, y) 直接传百分比
* - heroFocusCss('center') 兼容旧字符串
*/
export function heroFocusCss(a, b) {
if (a && typeof a === 'object') {
return `${clampHeroFocus(a.heroFocusX, 50)}% ${clampHeroFocus(a.heroFocusY, 50)}%`
}
if (typeof a === 'string' && LEGACY_HERO_FOCUS[a]) {
const [x, y] = LEGACY_HERO_FOCUS[a]
return `${x}% ${y}%`
}
return `${clampHeroFocus(a, 50)}% ${clampHeroFocus(b, 50)}%`
}
export function resolveHeroFocusXY(loaded = {}) {
const hasXY = Number.isFinite(Number(loaded.heroFocusX)) && Number.isFinite(Number(loaded.heroFocusY))
if (hasXY) {
return {
heroFocusX: clampHeroFocus(loaded.heroFocusX),
heroFocusY: clampHeroFocus(loaded.heroFocusY),
}
}
const legacy = LEGACY_HERO_FOCUS[loaded.heroFocus] || LEGACY_HERO_FOCUS.center
return { heroFocusX: legacy[0], heroFocusY: legacy[1] }
}
/** 解析为 1 | 2 | 3无法识别返回 0 */
export function resolvePosterSide(input) {
if (input === 1 || input === '1') return 1
@@ -47,6 +116,9 @@ function baseFields(side) {
mobileLayout: 'auto',
enabled: true,
heroImg: '',
/** 主图焦点百分比 0100object-position */
heroFocusX: 50,
heroFocusY: 50,
/** 二维码中心图;空则显示圆圈大囍 */
qrCenterImg: '',
title: '婚礼邀请函',
@@ -269,6 +341,7 @@ export function normalizePosterConfig(raw, side = 1) {
pageTitle: loaded.pageTitle || base.pageTitle || '',
pageDescription: loaded.pageDescription || base.pageDescription || '',
qrCenterImg: loaded.qrCenterImg || '',
...resolveHeroFocusXY(loaded),
verticalSlogan: loaded.verticalSlogan || base.verticalSlogan,
subSlogan1: loaded.subSlogan1 || base.subSlogan1,
subSlogan2: loaded.subSlogan2 || base.subSlogan2,