| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 | var Animator = require("../animation/Animator");var logError = require("../core/log");var _util = require("../core/util");var isString = _util.isString;var isFunction = _util.isFunction;var isObject = _util.isObject;var isArrayLike = _util.isArrayLike;var indexOf = _util.indexOf;/** * @alias module:zrender/mixin/Animatable * @constructor */var Animatable = function () {  /**   * @type {Array.<module:zrender/animation/Animator>}   * @readOnly   */  this.animators = [];};Animatable.prototype = {  constructor: Animatable,  /**   * 动画   *   * @param {string} path The path to fetch value from object, like 'a.b.c'.   * @param {boolean} [loop] Whether to loop animation.   * @return {module:zrender/animation/Animator}   * @example:   *     el.animate('style', false)   *         .when(1000, {x: 10} )   *         .done(function(){ // Animation done })   *         .start()   */  animate: function (path, loop) {    var target;    var animatingShape = false;    var el = this;    var zr = this.__zr;    if (path) {      var pathSplitted = path.split('.');      var prop = el; // If animating shape      animatingShape = pathSplitted[0] === 'shape';      for (var i = 0, l = pathSplitted.length; i < l; i++) {        if (!prop) {          continue;        }        prop = prop[pathSplitted[i]];      }      if (prop) {        target = prop;      }    } else {      target = el;    }    if (!target) {      logError('Property "' + path + '" is not existed in element ' + el.id);      return;    }    var animators = el.animators;    var animator = new Animator(target, loop);    animator.during(function (target) {      el.dirty(animatingShape);    }).done(function () {      // FIXME Animator will not be removed if use `Animator#stop` to stop animation      animators.splice(indexOf(animators, animator), 1);    });    animators.push(animator); // If animate after added to the zrender    if (zr) {      zr.animation.addAnimator(animator);    }    return animator;  },  /**   * 停止动画   * @param {boolean} forwardToLast If move to last frame before stop   */  stopAnimation: function (forwardToLast) {    var animators = this.animators;    var len = animators.length;    for (var i = 0; i < len; i++) {      animators[i].stop(forwardToLast);    }    animators.length = 0;    return this;  },  /**   * Caution: this method will stop previous animation.   * So do not use this method to one element twice before   * animation starts, unless you know what you are doing.   * @param {Object} target   * @param {number} [time=500] Time in ms   * @param {string} [easing='linear']   * @param {number} [delay=0]   * @param {Function} [callback]   * @param {Function} [forceAnimate] Prevent stop animation and callback   *        immediently when target values are the same as current values.   *   * @example   *  // Animate position   *  el.animateTo({   *      position: [10, 10]   *  }, function () { // done })   *   *  // Animate shape, style and position in 100ms, delayed 100ms, with cubicOut easing   *  el.animateTo({   *      shape: {   *          width: 500   *      },   *      style: {   *          fill: 'red'   *      }   *      position: [10, 10]   *  }, 100, 100, 'cubicOut', function () { // done })   */  // TODO Return animation key  animateTo: function (target, time, delay, easing, callback, forceAnimate) {    animateTo(this, target, time, delay, easing, callback, forceAnimate);  },  /**   * Animate from the target state to current state.   * The params and the return value are the same as `this.animateTo`.   */  animateFrom: function (target, time, delay, easing, callback, forceAnimate) {    animateTo(this, target, time, delay, easing, callback, forceAnimate, true);  }};function animateTo(animatable, target, time, delay, easing, callback, forceAnimate, reverse) {  // animateTo(target, time, easing, callback);  if (isString(delay)) {    callback = easing;    easing = delay;    delay = 0;  } // animateTo(target, time, delay, callback);  else if (isFunction(easing)) {      callback = easing;      easing = 'linear';      delay = 0;    } // animateTo(target, time, callback);    else if (isFunction(delay)) {        callback = delay;        delay = 0;      } // animateTo(target, callback)      else if (isFunction(time)) {          callback = time;          time = 500;        } // animateTo(target)        else if (!time) {            time = 500;          } // Stop all previous animations  animatable.stopAnimation();  animateToShallow(animatable, '', animatable, target, time, delay, reverse); // Animators may be removed immediately after start  // if there is nothing to animate  var animators = animatable.animators.slice();  var count = animators.length;  function done() {    count--;    if (!count) {      callback && callback();    }  } // No animators. This should be checked before animators[i].start(),  // because 'done' may be executed immediately if no need to animate.  if (!count) {    callback && callback();  } // Start after all animators created  // Incase any animator is done immediately when all animation properties are not changed  for (var i = 0; i < animators.length; i++) {    animators[i].done(done).start(easing, forceAnimate);  }}/** * @param {string} path='' * @param {Object} source=animatable * @param {Object} target * @param {number} [time=500] * @param {number} [delay=0] * @param {boolean} [reverse] If `true`, animate *        from the `target` to current state. * * @example *  // Animate position *  el._animateToShallow({ *      position: [10, 10] *  }) * *  // Animate shape, style and position in 100ms, delayed 100ms *  el._animateToShallow({ *      shape: { *          width: 500 *      }, *      style: { *          fill: 'red' *      } *      position: [10, 10] *  }, 100, 100) */function animateToShallow(animatable, path, source, target, time, delay, reverse) {  var objShallow = {};  var propertyCount = 0;  for (var name in target) {    if (!target.hasOwnProperty(name)) {      continue;    }    if (source[name] != null) {      if (isObject(target[name]) && !isArrayLike(target[name])) {        animateToShallow(animatable, path ? path + '.' + name : name, source[name], target[name], time, delay, reverse);      } else {        if (reverse) {          objShallow[name] = source[name];          setAttrByPath(animatable, path, name, target[name]);        } else {          objShallow[name] = target[name];        }        propertyCount++;      }    } else if (target[name] != null && !reverse) {      setAttrByPath(animatable, path, name, target[name]);    }  }  if (propertyCount > 0) {    animatable.animate(path, false).when(time == null ? 500 : time, objShallow).delay(delay || 0);  }}function setAttrByPath(el, path, name, value) {  // Attr directly if not has property  // FIXME, if some property not needed for element ?  if (!path) {    el.attr(name, value);  } else {    // Only support set shape or style    var props = {};    props[path] = {};    props[path][name] = value;    el.attr(props);  }}var _default = Animatable;module.exports = _default;
 |