| 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 modue: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; |