function AnimationControllerFactory()

in src/components/common/animation-control/animation-controller.js [26:165]


function AnimationControllerFactory() {
  class AnimationController extends Component {
    static defaultProps = {
      baseSpeed: BASE_SPEED,
      speed: 1,
      animationType: ANIMATION_TYPE.continuous
    };

    state = {
      isAnimating: false
    };

    componentDidUpdate() {
      if (!this._timer && this.state.isAnimating) {
        if (this.props.animationType === ANIMATION_TYPE.continuous) {
          this._timer = requestAnimationFrame(this._nextFrame);
        } else {
          // animate by interval
          // 30*600
          const {steps, speed} = this.props;
          if (!Array.isArray(steps) || !steps.length) {
            Console.warn('animation steps should be an array');
            return;
          }
          // when speed = 1, animation should loop through 600 frames at 60 FPS
          // calculate delay based on # steps
          const delay = (BASE_SPEED * (1000 / FPS)) / steps.length / (speed || 1);
          this._animate(delay);
        }
      }
    }

    _timer = null;

    _animate = delay => {
      this._startTime = new Date().getTime();

      const loop = () => {
        const current = new Date().getTime();
        // @ts-ignore
        const delta = current - this._startTime;

        if (delta >= delay) {
          this._nextFrame();
          this._startTime = new Date().getTime();
        } else {
          this._timer = requestAnimationFrame(loop);
        }
      };

      this._timer = requestAnimationFrame(loop);
    };

    _resetAnimationWindow = () => {
      const {domain, value} = this.props;
      if (Array.isArray(value)) {
        const value0 = domain[0];
        const value1 = value0 + value[1] - value[0];
        this.props.updateAnimation([value0, value1]);
      } else {
        this.props.updateAnimation(domain[0]);
      }
    };

    _resetAnimtionStep = () => {
      // go to the first steps
      this.props.updateAnimation([this.props.steps[0], 0]);
    };

    _resetAnimation = () => {
      if (this.props.animationType === ANIMATION_TYPE.continuous) {
        this._resetAnimationWindow();
      } else {
        this._resetAnimtionStep();
      }
    };

    _startAnimation = () => {
      this._pauseAnimation();
      if (typeof this.props.startAnimation === 'function') {
        this.props.startAnimation();
      }
      this.setState({isAnimating: true});
    };

    _pauseAnimation = () => {
      if (this._timer) {
        cancelAnimationFrame(this._timer);
        if (typeof this.props.pauseAnimation === 'function') {
          this.props.pauseAnimation();
        }
        this._timer = null;
      }
      this.setState({isAnimating: false});
    };

    _nextFrame = () => {
      this._timer = null;
      const nextValue =
        this.props.animationType === ANIMATION_TYPE.continuous
          ? this._nextFrameByWindow()
          : this._nextFrameByStep();

      this.props.updateAnimation(nextValue);
    };

    _nextFrameByWindow() {
      const {domain, value, speed, baseSpeed} = this.props;
      const delta = ((domain[1] - domain[0]) / baseSpeed) * speed;

      // loop when reaches the end
      if (Array.isArray(value)) {
        const value0 = value[1] + delta > domain[1] ? domain[0] : value[0] + delta;
        const value1 = value0 + value[1] - value[0];
        return [value0, value1];
      }
      return value + delta > domain[1] ? domain[0] : value + delta;
    }

    _nextFrameByStep() {
      const {steps, value} = this.props;
      const val = Array.isArray(value) ? value[0] : value;
      const index = bisectLeft(steps, val);
      const nextIdx = index >= steps.length - 1 ? 0 : index + 1;

      return [steps[nextIdx], nextIdx];
    }

    render() {
      const {isAnimating} = this.state;
      const {children} = this.props;

      return typeof children === 'function'
        ? children(isAnimating, this._startAnimation, this._pauseAnimation, this._resetAnimation)
        : null;
    }
  }

  return AnimationController;
}