Home Reference Source Repository

js/QuartzCore/CABasicAnimation.js

'use strict'

import CAPropertyAnimation from './CAPropertyAnimation'

/**
 * An object that provides basic, single-keyframe animation capabilities for a layer property. 
 * @access public
 * @extends {CAPropertyAnimation}
 * @see https://developer.apple.com/documentation/quartzcore/cabasicanimation
 */
export default class CABasicAnimation extends CAPropertyAnimation {

  /**
   * constructor
   * @access public
   * @param {?string} path -
   * @constructor
   */
  constructor(path) {
    super(path)

    // Interpolation values

    /**
     * Defines the value the receiver uses to start interpolation.
     * @type {?Object}
     * @see https://developer.apple.com/documentation/quartzcore/cabasicanimation/1412519-fromvalue
     */
    this.fromValue = null

    /**
     * Defines the value the receiver uses to end interpolation.
     * @type {?Object}
     * @see https://developer.apple.com/documentation/quartzcore/cabasicanimation/1412523-tovalue
     */
    this.toValue = null

    /**
     * Defines the value the receiver uses to perform relative interpolation.
     * @type {?Object}
     * @see https://developer.apple.com/documentation/quartzcore/cabasicanimation/1412445-byvalue
     */
    this.byValue = null

    this._baseValue = null
  }

  /**
   * @access public
   * @returns {CABasicAnimation} -
   */
  copy() {
    const anim = super.copy()
    //anim._copyValue(this)

    anim.fromValue = this.fromValue
    anim.toValue = this.toValue
    anim.byValue = this.byValue

    return anim
  }

  /*
  _copyValue(src) {
    this.fromValue = src.fromValue
    this.toValue = src.toValue
    this.byValue = src.byValue
  }
  */

  /**
   * apply animation to the given node.
   * @access private
   * @param {Object} obj - target object to apply this animation.
   * @param {number} time - active time
   * @param {boolean} [needTimeConversion = true] -
   * @returns {void}
   */
  _applyAnimation(obj, time, needTimeConversion = true) {
    let t = time
    if(needTimeConversion){
      const baseTime = this._basetimeFromTime(time)
      if(baseTime === null){
        return
      }
      t = baseTime
      if(this.timingFunction !== null){
        t = this.timingFunction._getValueAtTime(baseTime)
      }
      if(t < 0){
        t = 0
      }
      if(t > 1){
        t = 1
      }
      //if(this.keyPath === 'rotation.w'){
      //  console.log(`time: ${time}, activeTime: ${time - this._animationStartTime}, baseTime: ${baseTime}, t: ${t}`)
      //}
    }

    let isObject = false
    if(this._baseValue === null || this.isAdditive){
      this._baseValue = obj.valueForKeyPath(this.keyPath, false)
    }
    if(typeof this._baseValue !== 'number' && this._baseValue !== null){
      isObject = true
    }

    let fromValue = 0
    let toValue = 0
    if(this.fromValue !== null && this.toValue !== null){
      fromValue = this.fromValue
      toValue = this.toValue
    }else if(this.fromValue !== null && this.byValue !== null){
      fromValue = this.fromValue
      if(isObject){
        toValue = this.fromValue.add(this.byValue)
      }else{
        toValue = this.fromValue + this.byValue
      }
    }else if(this.byValue !== null && this.toValue !== null){
      if(isObject){
        fromValue = this.toValue.sub(this.byValue)
      }else{
        fromValue = this.toValue - this.byValue
      }
      toValue = toValue
    }else if(this.fromValue !== null){
      fromValue = this.fromValue
      if(this.isAdditive){
        if(isObject){
          toValue = this._baseValue.zero()
        }else{
          toValue = 0
        }
      }else{
        toValue = this._baseValue
      }
    }else if(this.toValue !== null){
      if(this.isAdditive){
        if(isObject){
          fromValue = this._baseValue.zero()
        }else{
          fromValue = 0
        }
      }else{
        fromValue = this._baseValue
      }
      toValue = this.toValue
    }else if(this.byValue !== null){
      if(this.isAdditive){
        if(isObject){
          fromValue = this._baseValue.zero()
        }else{
          fromValue = 0
        }
        toValue = this.byValue
      }else{
        fromValue = this._baseValue
        if(isObject){
          toValue = this._baseValue.add(this.byValue)
        }else{
          toValue = this._baseValue + this.byValue
        }
      }
    }else{
      // TODO: retain prevValue
      //value = this._lerp(prevValue, currentValue, t)
    }
    let value = this._lerp(fromValue, toValue, t)

    //if(this.keyPath === 'rotation.w'){
    //  console.log(`from: ${fromValue}, to: ${toValue}, t: ${t}, value: ${value}`)
    //}

    if(this.isAdditive){
      if(isObject){
        //value = value.add(obj.valueForKeyPath(this.keyPath))
        value = value.add(this._baseValue)
      }else{
        value += this._baseValue
      }
    }

    //if(this.keyPath === 'rotation.w'){
    //  console.log(`value after: ${value}`)
    //}

    //console.log(`CABasicAnimation._applyAnimation: keyPath: ${this.keyPath}, time: ${time}, baseTime: ${baseTime}, t: ${t}, value: ${value}`)
    this._applyValue(obj, value)
    this._handleEvents(obj, t)
  }
}