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