41 lines
1.3 KiB
TypeScript
41 lines
1.3 KiB
TypeScript
function clampInt(n: number, min: number, max: number): number {
|
||
if (!Number.isFinite(n)) return min;
|
||
return Math.min(max, Math.max(min, Math.floor(n)));
|
||
}
|
||
|
||
function hashStringToInt32(input: string): number {
|
||
// 简单可复现 hash:用于把 seed 映射为偏移量(不用于安全场景)
|
||
let h = 0;
|
||
for (let i = 0; i < input.length; i += 1) {
|
||
// eslint-disable-next-line no-bitwise
|
||
h = (h * 31 + input.charCodeAt(i)) | 0;
|
||
}
|
||
return h;
|
||
}
|
||
|
||
/**
|
||
* 生成 t ∈ [0,1],用于 Color(t)=lerp(top,bottom,t)
|
||
*
|
||
* 说明:
|
||
* - Home 没有 scroll,用“切换文案 step_index”模拟连续流动
|
||
* - 采用往返波形,避免从 1 回到 0 的突跳
|
||
*/
|
||
export function computeTFromStep(args: {
|
||
stepIndex: number;
|
||
segments?: number; // N,默认 12
|
||
seed?: string; // 允许用 seed 做初始相位偏移(同一次冷启动内稳定)
|
||
}): number {
|
||
const N = clampInt(args.segments ?? 12, 3, 60);
|
||
const stepIndex = clampInt(args.stepIndex, 0, 1_000_000_000);
|
||
const period = 2 * (N - 1);
|
||
|
||
const seed = String(args.seed ?? '');
|
||
const offset = seed ? Math.abs(hashStringToInt32(seed)) % period : 0;
|
||
|
||
const phase = (stepIndex + offset) % period;
|
||
const up = phase <= (N - 1);
|
||
const pos = up ? phase : period - phase;
|
||
return pos / (N - 1);
|
||
}
|
||
|