topfans/frontend/utils/laser-card/laserGrating.js
2026-06-03 22:19:22 +08:00

91 lines
2.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 镭射光栅参数模块 — 真实物理参考
* 实体镭射卡的底色是金属银/香槟金系(不是彩色),
* 彩虹只在视角变化时形成单一色带扫过。
*/
export const DEFAULT_BACKDROP_TONE = '#A8ACB2' // 冷银(深银)
/**
* 金属底色系(深银,真实镭射卡"金属膜"质感)
* 越接近实物的"银" —— 不再是浅灰偏白
*/
export const METAL_PALETTE = [
'#A8ACB2', // 冷银
'#A89E91', // 香槟银
'#9C9993', // 暖银
'#B0ACA6', // 珍珠银
'#9E958A', // 玫瑰金底
]
/**
* sheenBandAngle单一彩虹色带方向与卡面平行方向彩虹带在它法线方向上扫过
* sheenSpeed彩虹色带扫过卡面的速度
* sheenIntensity彩虹色带亮度实体卡通常 0.25-0.5
* foilCoveragefoil 覆盖比例 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
}