effects/effect.js

import _ from 'lodash';
import BarEffect from './bar';
import PieEffect from './pie';
import Utility from '../utility';
import LineEffect from './line';
import ScatterEffect from './scatter';

/**
 * GamifyJS에서 효과를 생성하는 클래스입니다.
 * <p>해당 클래스를 통해 효과를 생성하고, 제어할 수 있습니다.
 * @class
 * @alias Effect
 */
export default class Effect {
  /**
   * @description DOM의 크기를 계산해서 DOM의 크기에 딱 맞게 Gamify를 생성해준다.
   * @param  { object } option - Gamify를 통해 생설할 차트의 데이터
   * @param  { ChartJS } chartJsObject - 생성된 ChartJS 오브젝트
   */
  constructor(option, chartJsObject) {
    /**
     * @description Gamify를 통해 생설할 차트의 데이터
     * @type { object }
     */
    this.option = option;

    /**
     * @description 차트의 타입
     * @type { string }
     */
    this.chartType = option.type;

    /**
     * @description 생성된 ChartJS 오브젝트
     * @type { ChartJS }
     */
    this.chartJsObject = chartJsObject;

    /**
     * @description 효과를 생성할 PIXI 오브젝트
     * @type { PIXI }
     */
    this.pixiJsObject = Utility.makePixi(option);

    /**
     * @description 각 타입의 효과 정보
     * @type { object }
     * @property { string } bar bar 타입에 대한 효과 정보
     * @property { array } bar.enableEffectList bar 타입에서 사용가능한 효과 리스트
     * @property { string } bar.enableEffectList.upperLimit 기준치 초과에 대한 효과
     * @property { string } bar.enableEffectList.lowerLimit 기준치 미만에 대한 효과
     * @property { BarEffect } bar.Effect BarEffect 오브젝트
     * @property { string } pie pie 타입에 대한 효과 정보
     * @property { array } pie.enableEffectList pie 타입에서 사용가능한 효과 리스트
     * @property { string } pie.enableEffectList.extrude 마우스 상호작용 효과
     * @property { PieEffect } pie.Effect PieEffect 오브젝트
     * @property { string } scatter scatter 타입에 대한 효과 정보
     * @property { array } scatter.enableEffectList scatter 타입에서 사용가능한 효과 리스트
     * @property { string } scatter.enableEffectList.missile scatter 차트의 좌표가 변경될 때효과
     * @property { ScatterEffect } scatter.Effect scatterEffect 오브젝트
     */
    this.information = {
      bar: {
        enableEffectList: ['lowerLimit', 'upperLimit'],
        enableEffectAgain: [],
        Effect: BarEffect,
      },
      line: {
        enableEffectList: ['lineTracer', 'lowerLimit', 'upperLimit'],
        enableEffectAgain: ['lineTracer'],
        Effect: LineEffect,
      },
      pie: {
        enableEffectList: ['extrude'],
        enableEffectAgain: [],
        Effect: PieEffect,
      },
      scatter: {
        enableEffectList: ['missile'],
        enableEffectAgain: [],
        Effect: ScatterEffect,
      },
    };


    /**
     * @description 생성된 효과의 리스트
     * @type { object }
     */
    this.list = {};

    /**
     * @description ChartJS 애니메이션 onProgress 이벤트 발생 시 실행되는 함수 리스트
     * @type { object }
     */
    this.chartOnProgressFunctionList = {};

    /**
     * @description ChartJS 애니메이션 onComplete 이벤트 발생 시 실행되는 함수 리스트
     * @type { object }
     */
    this.chartOnCompleteFunctionList = {};
    this.chartJsObject.options.animation.onProgress = () => {
      for (let i = 0; i < Object.keys(this.chartOnProgressFunctionList).length; i++) {
        const key = Object.keys(this.chartOnProgressFunctionList)[i];
        this.chartOnProgressFunctionList[key]();
      }
    };

    this.chartJsObject.options.animation.onComplete = () => {
      for (let i = 0; i < Object.keys(this.chartOnCompleteFunctionList).length; i++) {
        const key = Object.keys(this.chartOnCompleteFunctionList)[i];
        this.chartOnCompleteFunctionList[key]();
      }
    };

    const standardResolution = {
      width: 1280,
      height: 720,
    };
    const { width, height } = this.option.rootSize;
    this.widthRatio = width / standardResolution.width;
    this.heightRatio = height / standardResolution.height;
    this.ratio = {
      min: Math.min(this.widthRatio, this.heightRatio),
      mid: Math.min(this.widthRatio, this.heightRatio) + Math.abs(this.widthRatio - this.heightRatio) / 2,
      max: Math.max(this.widthRatio, this.heightRatio),
    };

    /**
     * @description 효과 리스트 인덱스
     * @type { number }
     */
    this.effectListIndex = 0;

    /**
     * @description ChartJS onProgress 추가 인덱스
     * @type { number }
     */
    this.chartAnimationProgressIndex = 0;

    /**
     * @description ChartJS onComplete 추가 인덱스
     * @type { number }
     */
    this.chartAnimationCompleteIndex = 0;
  }

  /**
   * 효과를 추가하는 함수
   * <p>사용하고자 하는 효과를 알맞은 차트에 적용해야함
   * @param {effectName} option 효과 이름
   * @param {object} option 효과 옵션
   * @memberof Effect
   * @returns {number} 인덱스 반환
   * @instance
   */
  add(effectName, option) {
    option = option || {};
    if (_.includes(this.information[this.chartType].enableEffectList, effectName)) {
      if (!_.isUndefined(this.information[this.chartType].enableEffectAgain) && !_.includes(this.information[this.chartType].enableEffectAgain, effectName)) {
        for (let i = 0; i < Object.keys(this.list).length; i++) {
          if (_.isEqual(effectName, this.list[Object.keys(this.list)[i]].name)) {
            console.warn(`${effectName} 는 중복적용을 지원하지 않습니다.`);
            return undefined;
          }
        }
      }
      const effect = new this.information[this.chartType].Effect({ chart: this.chartJsObject, pixi: this.pixiJsObject, effect: this }, option);
      effect[effectName]();
      this.list[this.effectListIndex++] = { object: effect, name: effectName };
      return this.effectListIndex - 1;
    }
    console.warn('지원하지 않는 효과입니다.');
    return undefined;
  }

  /**
   * 효과를 제거하는 함수
   * @param {number} index 효과 생성할 때 얻은 인덱스
   * @memberof Effect
   * @instance
   */
  destroy(index) {
    this.list[index].object.destroy();
    delete this.list[index];
  }

  /**
   * <b>내부 함수</b>
   * <p>ChartJS 애니메이션 onProgress 이벤트 발생할 때 실행할 함수를 추가하는 함수
   * @param {function} fn 실행할 함수
   * @memberof Effect
   * @returns {number} 인덱스
   * @instance
   */
  _addOnProgressFunction(fn) {
    this.chartOnProgressFunctionList[this.chartAnimationProgressIndex++] = fn;
    return this.chartAnimationProgressIndex - 1;
  }

  /**
   * <b>내부 함수</b>
   * <p>ChartJS 애니메이션 onComplete 이벤트 발생할 때 실행할 함수를 추가하는 함수
   * @param {function} fn 실행할 함수
   * @memberof Effect
   * @returns {number} 인덱스
   * @instance
   */
  _addOnCompleteFunction(fn) {
    this.chartOnCompleteFunctionList[this.chartAnimationCompleteIndex++] = fn;
    return this.chartAnimationCompleteIndex - 1;
  }

  /**
   * <b>내부 함수</b>
   * <p>ChartJS 애니메이션 onProgress 이벤트 발생할 때 실행할 함수를 제거하는 함수
   * @param {number} index _addOnProgressFunction를 통해 받은 인덱스
   * @memberof Effect
   * @instance
   */
  _removeOnProgressFunction(index) {
    delete this.chartOnProgressFunctionList[index];
  }

  /**
   * <b>내부 함수</b>
   * <p>ChartJS 애니메이션 onComplete 이벤트 발생할 때 실행할 함수를 제거하는 함수
   * @param {number} index _addOnCompleteFunction 통해 받은 인덱스
   * @memberof Effect
   * @instance
   */
  _removeOnCompleteFunction(index) {
    delete this.chartOnCompleteFunctionList[index];
  }
}