/** * 镭射光栅参数模块 — 真实物理参考 * 实体镭射卡的底色是金属银/香槟金系(不是彩色), * 彩虹只在视角变化时形成单一色带扫过。 */ export const DEFAULT_BACKDROP_TONE = '#A8ACB2' // 冷银(深银) /** * 金属底色系(深银,真实镭射卡"金属膜"质感) * 越接近实物的"银" —— 不再是浅灰偏白 */ export const METAL_PALETTE = [ '#A8ACB2', // 冷银 '#A89E91', // 香槟银 '#9C9993', // 暖银 '#B0ACA6', // 珍珠银 '#9E958A', // 玫瑰金底 ] /** * sheenBandAngle:单一彩虹色带方向(与卡面平行方向;彩虹带在它法线方向上扫过) * sheenSpeed:彩虹色带扫过卡面的速度 * sheenIntensity:彩虹色带亮度(实体卡通常 0.25-0.5) * foilCoverage:foil 覆盖比例 0-1(>0.6 表示满版 foil,<0.4 表示局部 foil) */ export function resolveGratingConfig(preset, variantIndex = 0) { const p = preset || {} const style = String(p.style || 'dream') // 金属底色 const toneSeed = (style.charCodeAt(0) * 3 + variantIndex * 7) % METAL_PALETTE.length const backdropTone = METAL_PALETTE[toneSeed] // 单色带方向:preset 给一个基础值,每张卡 ±5° 抖动 const baseAngle = Number(p.sheenBandAngle ?? p.stripeAngle ?? 135) const angleJitter = ((variantIndex * 13) % 11) - 5 const sheenBandAngle = ((baseAngle + angleJitter) % 180 + 180) % 180 // 色带扫过速度 const sheenSpeed = Math.max(0.1, Math.min(1.5, Number(p.sheenSpeed ?? 0.4))) // 彩虹强度(实体卡不超过 0.5) const sheenIntensity = Math.max(0, Math.min(0.6, Number(p.sheenIntensity ?? 0.35))) // foil 覆盖比例 const foilCoverage = Math.max(0.2, Math.min(1.0, Number(p.foilCoverage ?? 0.7))) // stripePhase 保留以兼容旧字段 const stripePhase = variantIndex * 0.37 + style.charCodeAt(0) * 0.019 return { backdropTone, sheenBandAngle, sheenSpeed, sheenIntensity, foilCoverage, // 兼容旧字段(不强依赖) stripeAngle: sheenBandAngle, stripeDensity: 0, stripeIntensity: sheenIntensity, holoBlend: 'screen', flowSpeed: sheenSpeed, stripePhase, } } export function hexToRgb(hex) { const h = String(hex || DEFAULT_BACKDROP_TONE).replace('#', '') const n = parseInt( h.length === 3 ? h.split('').map((c) => c + c).join('') : h, 16 ) return [(n >> 16) & 255, (n >> 8) & 255, n & 255] } /** 单色带宽度(px),实体镭射卡的对角色带通常 30-60px 宽 */ export function sheenBandWidthPx(cw) { return Math.max(20, Math.min(80, cw * 0.18)) } /** 兼容旧 API */ export function stripeWidthPx(cw, density) { return sheenBandWidthPx(cw) } /** 兼容旧 API */ export function phaseOffset(timeMs, flowSpeed, stripePhase) { return ((timeMs * 0.001 * flowSpeed * 0.5) + stripePhase) % 1 }