Source: animation/PathMotion.js

import {Animate} from './Animate';
import {Utils} from '../utils/Utils';
import {Tween} from '../utils/Tween';
import {Curve} from '../math/Curve';
import {Point} from '../math/Point';

/**
 * PathMotion类型动画对象
 *
 * @class
 * @memberof JC
 * @param {object} [options] 动画所具备的特性
 */
function PathMotion(options) {
  Animate.call(this, options);
  if (!options.path || !(options.path instanceof Curve)) {
    console.warn('path is not instanceof Curve');
  }

  this.path = options.path;

  this.ease = options.ease || Tween.Ease.InOut;

  this.lengthMode = Utils.isBoolean(options.lengthMode)?
    options.lengthMode:
    false;

  this.attachTangent = Utils.isBoolean(options.attachTangent)?
    options.attachTangent:
    false;

  this._cacheRotate = this.element.rotation;
  const radian = this._cacheRotate;
  this._cacheVector = new Point(10 * Math.cos(radian), 10 * Math.sin(radian));
}

PathMotion.prototype = Object.create(Animate.prototype);

/**
 * 计算下一帧状态
 * @private
 * @return {object}
 */
PathMotion.prototype.nextPose = function() {
  let _rotate = 0;
  const t = this.ease(this.progress / this.duration);
  const pos = this.lengthMode ?
    this.path.getPointAt(t) :
    this.path.getPoint(t);

  const pose = pos.clone();

  if (this.attachTangent) {
    _rotate = this.decomposeRotate(t);
    pose.rotation = _rotate === false ? this.preDegree : _rotate;
    pose.rotation += this._cacheRotate;
    if (_rotate !== false) this.preDegree = _rotate;
  }
  this.element.setProps(pose);
  return pose;
};

/**
 * 解算旋转角度
 * @private
 * @param {number} t 当前进度, 区间[0, 1]
 * @return {number}
 */
PathMotion.prototype.decomposeRotate = function(t) {
  const vector = this.lengthMode ?
    this.path.getTangentAt(t) :
    this.path.getTangent(t);

  const nor = this._cacheVector.x * vector.y - vector.x * this._cacheVector.y;
  const pi = nor > 0 ? 1 : -1;
  const cos = (vector.x * this._cacheVector.x + vector.y * this._cacheVector.y)/
    ( Math.sqrt(vector.x * vector.x + vector.y * vector.y) *
      Math.sqrt(
        this._cacheVector.x * this._cacheVector.x +
        this._cacheVector.y * this._cacheVector.y
      )
    );
  if (isNaN(cos)) return false;
  return pi * Math.acos(cos) * Utils.RTD;
};

export {PathMotion};