2026-10-03 17:47:24 +00:00

57 lines
1.8 KiB
TypeScript

/** Easing and timing helpers shared by every motion template. All pure. */
export function clamp(value: number, min = 0, max = 1): number {
return Math.min(max, Math.max(min, value));
}
export function lerp(from: number, to: number, amount: number): number {
return from + (to - from) * amount;
}
/** Linear progress 0..1 of `t` through the window [start, end]. */
export function progress(t: number, start: number, end: number): number {
if (end <= start) return t >= end ? 1 : 0;
return clamp((t - start) / (end - start));
}
export function easeInCubic(x: number): number {
return x * x * x;
}
export function easeOutCubic(x: number): number {
return 1 - (1 - x) ** 3;
}
export function easeInOutCubic(x: number): number {
return x < 0.5 ? 4 * x * x * x : 1 - (-2 * x + 2) ** 3 / 2;
}
export function easeOutExpo(x: number): number {
return x >= 1 ? 1 : 1 - 2 ** (-10 * x);
}
/** Overshoots slightly before settling; for pop-in reveals. */
export function easeOutBack(x: number): number {
const c1 = 1.70158;
const c3 = c1 + 1;
return 1 + c3 * (x - 1) ** 3 + c1 * (x - 1) ** 2;
}
/**
* Opacity envelope for a clip of `durationMs`: fades in over `inMs` and out
* over the last `outMs`.
*/
export function fadeEnvelope(t: number, durationMs: number, inMs: number, outMs: number): number {
const fadeIn = inMs > 0 ? easeOutCubic(progress(t, 0, inMs)) : 1;
const fadeOut = outMs > 0 ? 1 - easeInCubic(progress(t, durationMs - outMs, durationMs)) : 1;
return clamp(Math.min(fadeIn, fadeOut));
}
/** Deterministic pseudo-random value in [0, 1) for integer seeds. */
export function hash01(a: number, b = 0): number {
let h = Math.imul(a ^ 0x9e3779b9, 0x85ebca6b) ^ Math.imul(b + 0x632be5ab, 0xc2b2ae35);
h = Math.imul(h ^ (h >>> 15), 0x2c1b3c6d);
h = Math.imul(h ^ (h >>> 12), 0x297a2d39);
return ((h ^ (h >>> 15)) >>> 0) / 4294967296;
}