91 lines
2.8 KiB
JavaScript
91 lines
2.8 KiB
JavaScript
/**
|
||
* 镭射光栅参数模块 — 真实物理参考
|
||
* 实体镭射卡的底色是金属银/香槟金系(不是彩色),
|
||
* 彩虹只在视角变化时形成单一色带扫过。
|
||
*/
|
||
|
||
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
|
||
}
|