/**
* Плавный линейный переход значения к цели за заданное время.
* Чистый класс без Pixi (headless-тестируется): ambient/виньетка Lighting,
* оверлей переходов SceneManager — вместо ручных пар fadeLeft/fadeTotal.
*/
export class FadeState<T> {
private from: T;
/** Поле не может называться `to` — имя занято методом to(). */
private toVal: T;
private current: T;
private left = 0;
private total = 0;
constructor(
private readonly lerp: (a: T, b: T, k: number) => T,
initial: T
) {
this.from = this.toVal = this.current = initial;
}
/** Текущее значение (в процессе фейда — интерполированное). */
get value(): T {
return this.current;
}
/** Цель, заданная последним to(). */
get target(): T {
return this.toVal;
}
/** Начальное значение текущего фейда (на момент запуска). */
get origin(): T {
return this.from;
}
/** Идёт ли сейчас фейд. */
get active(): boolean {
return this.left > 0;
}
/** Задать цель; seconds > 0 — плавный лерп от текущего значения. */
to(target: T, seconds = 0): void {
this.from = this.current;
this.toVal = target;
this.total = Math.max(0, seconds);
this.left = this.total;
if (this.total === 0) this.current = target;
}
/** Тик; по завершении значение точно равно цели. */
update(dt: number): void {
if (this.left <= 0) return;
this.left = Math.max(0, this.left - dt);
const k = this.total > 0 ? 1 - this.left / this.total : 1;
this.current = this.lerp(this.from, this.toVal, k);
if (this.left === 0) this.current = this.toVal;
}
}