Newer
Older
rpg / packages / engine / src / core / fade.ts
/**
 * Плавный линейный переход значения к цели за заданное время.
 * Чистый класс без 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;
    }
}