/**
* Процедурные «оживители» статичных объектов — чистая математика без Pixi:
* синусные волны, композиция до четырёх одновременно. Фазы — в долях
* периода; случайная фаза берётся из seed снаружи (детерминизм).
*/
export interface BobParams {
/** Амплитуда, px (округляется до целого — пиксель-арт не дрожит). */
amplitude: number;
/** Полный период, сек. */
period: number;
/** Фаза, доли периода (0..1). */
phase?: number;
}
export interface SwayParams {
/** Амплитуда, рад (вокруг основания view). */
amplitude: number;
period: number;
phase?: number;
}
export interface PulseParams {
/** Нижняя и верхняя граница пульса (alpha или scale). */
min: number;
max: number;
period: number;
phase?: number;
}
export interface BlinkParams {
period: number;
/** Доля периода в «включённом» состоянии (0..1), по умолчанию 0.5. */
duty?: number;
phase?: number;
}
export interface MotionParams {
bob?: BobParams;
sway?: SwayParams;
pulse?: PulseParams;
blink?: BlinkParams;
}
/** Выборка оживителя в момент t (сек) относительно базового состояния. */
export interface MotionSample {
/** Смещение по Y, px (целое). */
y: number;
/** Поворот, рад. */
angle: number;
/** Множитель альфы (произведение пульса и строба). */
alpha: number;
/** Множитель масштаба. */
scale: number;
}
const TAU = Math.PI * 2;
/** Фаза волны 0..1 в момент t для периода period и стартовой фазы phase. */
function wave(t: number, period: number, phase = 0): number {
return ((t / period + phase) % 1 + 1) % 1;
}
/** Выборка всех оживителей в момент t; параметры независимы, эффекты перемножаются. */
export function sampleMotion(p: MotionParams, t: number): MotionSample {
let y = 0;
let angle = 0;
let alpha = 1;
let scale = 1;
if (p.bob) {
const w = wave(t, p.bob.period, p.bob.phase);
// Синус фазы 0..1 → -1..1; округление до целого px.
y = Math.round(Math.sin(w * TAU) * p.bob.amplitude);
}
if (p.sway) {
const w = wave(t, p.sway.period, p.sway.phase);
angle = Math.sin(w * TAU) * p.sway.amplitude;
}
if (p.pulse) {
const w = wave(t, p.pulse.period, p.pulse.phase);
// Треугольная волна 0..1..0 — пульс без резких скачков фазы.
const tri = 1 - Math.abs(w * 2 - 1);
const k = p.pulse.min + (p.pulse.max - p.pulse.min) * tri;
alpha *= k;
scale *= k;
}
if (p.blink) {
const w = wave(t, p.blink.period, p.blink.phase);
alpha *= w < (p.blink.duty ?? 0.5) ? 1 : 0;
}
return { y, angle, alpha, scale };
}