Source: display/Item.js

import { noop } from "../utils/helpers";
import { ItemTransformProps } from "../data/props/ItemTransformProps";
import { ColorProps } from "../data/props/ColorProps";
import { Matrix3Utilities } from "../geom/Matrix3Utilities";
import "../geom/RectangleType";

/**
 * Item
 * @property {Item.Type} TYPE
 * @property {Array<number>} matrixCache
 * @property {Array<number>} colorCache
 * @property {boolean} interactive
 * @property {boolean} renderable
 * @property {ItemTransformProps} transform
 * @property {ColorProps} color
 * @property {number} alpha
 * @property {boolean} transformUpdated
 * @property {boolean} colorUpdated
 * @property {function} callbackBeforeRender
 * @property {function} callbackAfterRender
 */
export class Item {
  /**
   * Creates an instance of Item.
   * @constructor
   */
  constructor() {
    this.RENDERING_TYPE = Item.RENDERING_TYPE;

    this.matrixCache = Matrix3Utilities.identity();
    this.colorCache = [1, 1, 1, 1];
    this.transform = new this.$transformClass();
    this.color = new ColorProps();
    this.alpha = 1;
    this.callbackBeforeRender = this.callbackAfterRender = noop;
    this.renderable = true;
    this.$bounds = { x: 0, y: 0, width: 0, height: 0 };
  }

  /**
   * Get StageContainer
   * @readonly
   * @type {StageContainer}
   */
  get stage() {
    return this.$parent ? this.$parent.stage : null;
  }

  /**
   * Set/Get parent
   * @type {Container}
   */
  get parent() {
    return this.$parent;
  }
  set parent(v) {
    this.$parent = v;
    this._parentChanged = true;
  }

  /**
   * <pre>
   *  Set/Get before render callback
   *    - It will be called before the Item rendered
   * </pre>
   * @type {function}
   */
  get callbackBeforeRender() {
    return this._callbackBeforeRender;
  }
  set callbackBeforeRender(v) {
    this._callbackBeforeRender = v ?? noop;
  }

  /**
   * <pre>
   *  Set/Get after render callback
   *    - It will be called after the Item rendered
   * </pre>
   * @type {function}
   */
  get callbackAfterRender() {
    return this._callbackAfterRender;
  }
  set callbackAfterRender(v) {
    this._callbackAfterRender = v ?? noop;
  }

  /**
   * <pre>
   *  Handle event
   *    - It can handle mouse events if the item is interactive and has [
   *      onPointerOver,
   *      onPointerOut,
   *      onPointerMove,
   *      onPointerDown,
   *      onPointerUp,
   *      onPointerClick
   *    ] function
   * </pre>
   * @param {*} event
   */
  callEventHandler(target, event) {
    if (this.interactive) {
      const callback = this["on" + event.type];
      callback && callback(this, target, event);
    }

    const parent = this.$parent;
    parent && parent.callEventHandler(target, event);
  }

  /**
   * Returns with the Item bounds
   * @returns {Rectangle}
   */
  getBounds() {
    return this.$bounds;
  }

  /**
   * Destruct class
   */
  destruct() {
    this.remove();
  }

  /**
   * Remove Item from parent
   */
  remove() {
    const parent = this.$parent;
    parent && parent.removeChild(this);
  }

  /**
   * Update color and transform values
   */
  update() {
    const transform = this.transform,
      color = this.color,
      parent = this.$parent,
      parentChanged = this._parentChanged;

    this._parentChanged = false;

    color.update();

    this.colorUpdated = parentChanged || color.updated || parent.colorUpdated;

    if (this.colorUpdated) {
      const colorCache = this.colorCache,
        originalColorCache = color.cache,
        parentColorCache = parent.colorCache;

      colorCache[0] = parentColorCache[0] * originalColorCache[0];
      colorCache[1] = parentColorCache[1] * originalColorCache[1];
      colorCache[2] = parentColorCache[2] * originalColorCache[2];
      colorCache[3] = parentColorCache[3] * originalColorCache[3];
    }

    transform.update();

    this.transformUpdated =
      parentChanged || transform.updated || parent.transformUpdated;

    this.transformUpdated &&
      Matrix3Utilities.transform(
        this.matrixCache,
        parent.matrixCache,
        transform
      );
  }

  /**
   * @ignore
   */
  get $transformClass() {
    return ItemTransformProps;
  }
}

/**
 * Type "item"
 * @string
 */
Item.RENDERING_TYPE = "item";