animations/tween/tween.js

import { Interpolation, Easing, Angle, Color, Vector2 } from "../../math/index.js"
/**
 * Component responsible for animations.
 * 
 * @template T
 */
export class Tween {
  /**
   * @type {number}
   */
  _duration = 0
  /**
   * @type {boolean}
   */
  _repeat = false
  /**
   * @type {boolean}
   */
  active = false
  /**
   * @type {T}
   * @private
   */
  _to
  /**
   * @type {T}
   * @private
   */
  _from
  /**
   * @type {T}
   * @private
   */
  _into
  /**
   * @type {LerpFunc}
   * @private
   */
  _interpolationFunc = Interpolation.Linear
  /**
   * @type {EasingFunc}
   * @private
   */
  _easingFunction = Easing.linear
  /**
   * @type {number}
   * @private
   */
  _timeTaken = 0
  /**
   * @type {TweenUpdate<T>}
   * @private
   */
  _updateFunc = NoUpdateThrow
  /**
   * @type {Tween<T> | null}
   * @private
   */
  _next = null
  /**
   * @param {T} into
   * @param {T} to
   * @param {T} from
   */
  constructor(to,from,into) {
    this._into = into
    this._to = to
    this._from = from
  }
  /**
   * @param {T} x
   */
  to(x) {
    this._to = x
    return this
  }
  /**
   * @param {T} x
   */
  from(x) {
    this._from = x
    return this
  }
  /**
   * @param {number} t
   */
  duration(t) {
    this._duration = t
    return this
  }
  repeat() {
    this._repeat = true
    return this
  }
  play() {
    this._timeTaken = 0
    this.active = true
  }
  stop() {
    this.active = false
  }
  /**
   * @param {TweenUpdate<T>} callback
   */
  onUpdate(callback) {
    this._updateFunc = callback
    return this
  }
  /**
   * @param {EasingFunc} callback
   */
  easing(callback) {
    this._easingFunction = callback
    return this
  }
  /**
   * @param {LerpFunc} callback
   */
  interpolant(callback) {
    this._interpolationFunc = callback
    return this
  }
  /**
   * @template U
   * @param {Tween<U>} tween
   * @param {number} dt
   */
  static update(tween,dt) {
    if (!tween.active) return

    tween._timeTaken += dt
    if (tween._timeTaken >= tween._duration) {
      if (tween._next) {
        tween.stop()
        tween._next.play()
      }
      if (tween._repeat) {
        tween._timeTaken = 0
      } else {
        tween._timeTaken = tween._duration
        tween.active = false
      }
    }
    let t = tween._easingFunction(
      tween._timeTaken / tween._duration
    )
    tween._updateFunc(
      tween._interpolationFunc,
      tween._to,
      tween._from,
      t,
      tween._into
    )
  }
  /**
   * @param {Tween<T>} next
   */
  chain(next) {
    this._next = next
    return this
  }
}
/**
 * @type {TweenUpdate<Vector2>}
 */
export function Vector2Update(lerpFunc, to, from, t, into) {
  console.log(t)
  into.x = lerpFunc(from.x, to.x, t)
  into.y = lerpFunc(from.y, to.y, t)
}
/**
 * @template T
 * 
 * @type {TweenUpdate<T>}
 */
export function Vector3Update(lerpFunc, to, from, t, into) {
  // @ts-ignore
  into.x = lerpFunc(from.x, to.x, t)
  // @ts-ignore
  into.y = lerpFunc(from.y, to.y, t)
  // @ts-ignore
  into.z = lerpFunc(from.z, to.z, t)
}

/**
 * @type {TweenUpdate<Color>}
 */
export function ColorUpdate(lerpFunc, to, from, t, into) {
  into.r = lerpFunc(from.r, to.r, t)
  into.g = lerpFunc(from.g, to.g, t)
  into.b = lerpFunc(from.b, to.b, t)
  into.a = lerpFunc(from.a, to.a, t)
}
/**
 *
 * @type {TweenUpdate<Angle>}
 */
export function AngleUpdate(lerpFunc, to, from, t, into) {
  into.value = lerpFunc(from.value, to.value, t)
}
/**
 * @type {TweenUpdate<any>}
 */
function NoUpdateThrow(lerpFunc, to, from, t, into) {
  throw "The Tween does not have a valid onUpdate callback."
}

/**
 * @template T
 * @callback TweenUpdate
 * @param {LerpFunc} lerpFunc
 * @param {T} to
 * @param {T} from
 * @param {number} t
 * @param {T} into
 * 
 * @returns {void}
 */
 
/**
 * @callback LerpFunc
 * @param {number} p0
 * @param {number} p1
 * @param {number} t
 * @returns {number}
 */