lepu-test-platform-web/node_modules/zrender/lib/mixin/Animatable.js

275 lines
7.2 KiB
JavaScript
Raw Normal View History

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;