|
let TweenAction = cc.Class({
|
name: 'cc.TweenAction',
|
extends: cc.ActionInterval,
|
|
ctor (duration, props, opts) {
|
this._opts = opts = opts || Object.create(null);
|
this._props = Object.create(null);
|
|
// global easing or progress used for this action
|
opts.progress = opts.progress || this.progress;
|
if (opts.easing && typeof opts.easing === 'string') {
|
opts.easing = cc.easing[opts.easing];
|
}
|
|
for (let name in props) {
|
let value = props[name];
|
|
// property may have custom easing or progress function
|
let easing, progress;
|
if (value.value && (value.easing || value.progress)) {
|
easing = typeof value.easing === 'string' ? cc.easing[value.easing] : value.easing;
|
progress = value.progress;
|
value = value.value;
|
}
|
|
let isNumber = typeof value === 'number';
|
if (!isNumber && (!value.lerp || (!value.add && !value.mul) || !value.clone)) {
|
cc.warn(`Can not animate ${name} property, because it do not have [lerp, (add|mul), clone] function.`);
|
continue;
|
}
|
|
let prop = Object.create(null);
|
prop.value = value;
|
prop.easing = easing;
|
prop.progress = progress;
|
this._props[name] = prop;
|
}
|
|
this._originProps = props;
|
this.initWithDuration(duration);
|
},
|
|
clone () {
|
var action = new TweenAction(this._duration, this._originProps, this._opts);
|
this._cloneDecoration(action);
|
return action;
|
},
|
|
startWithTarget (target) {
|
cc.ActionInterval.prototype.startWithTarget.call(this, target);
|
|
let relative = !!this._opts.relative;
|
let props = this._props;
|
for (let name in props) {
|
let value = target[name];
|
let prop = props[name];
|
|
if (typeof value === 'number') {
|
prop.start = value;
|
prop.current = value;
|
prop.end = relative ? value + prop.value : prop.value;
|
}
|
else {
|
prop.start = value.clone();
|
prop.current = value.clone();
|
prop.end = relative ? (value.add || value.mul).call(value, prop.value) : prop.value;
|
}
|
}
|
},
|
|
update (t) {
|
let opts = this._opts;
|
let easingTime = t;
|
if (opts.easing) easingTime = opts.easing(t);
|
|
let target = this.target;
|
if (!target) return;
|
|
let props = this._props;
|
let progress = this._opts.progress;
|
for (let name in props) {
|
let prop = props[name];
|
let time = prop.easing ? prop.easing(t) : easingTime;
|
let current = prop.current = (prop.progress || progress)(prop.start, prop.end, prop.current, time);
|
target[name] = current;
|
}
|
},
|
|
progress (start, end, current, t) {
|
if (typeof start === 'number') {
|
current = start + (end - start) * t;
|
}
|
else {
|
start.lerp(end, t, current);
|
}
|
return current;
|
}
|
});
|
|
|
|
/**
|
* !#en
|
* Tween provide a simple and flexible way to create action.
|
* Tween's api is more flexible than cc.Action:
|
* - Support creating an action sequence in chained api,
|
* - Support animate any objects' any properties, not limited to node's properties.
|
* By contrast, cc.Action needs to create a new action class to support new node property.
|
* - Support working with cc.Action,
|
* - Support easing and progress function,
|
* !#zh
|
* Tween 提供了一个简单灵活的方法来创建 action。
|
* 相对于 Cocos 传统的 cc.Action,cc.Tween 在创建动画上要灵活非常多:
|
* - 支持以链式结构的方式创建一个动画序列。
|
* - 支持对任意对象的任意属性进行缓动,不再局限于节点上的属性,而 cc.Action 添加一个属性的支持时还需要添加一个新的 action 类型。
|
* - 支持与 cc.Action 混用
|
* - 支持设置 easing 或者 progress 函数
|
* @class Tween
|
* @example
|
* cc.tween(node)
|
* .to(1, {scale: 2, position: cc.v3(100, 100, 100)})
|
* .call(() => { console.log('This is a callback'); })
|
* .by(1, {scale: 3, position: cc.v3(200, 200, 200)}, {easing: 'sineOutIn'})
|
* .run(cc.find('Canvas/cocos'));
|
*/
|
function Tween (target) {
|
this._actions = [];
|
this._finalAction = null;
|
this._target = target;
|
}
|
|
/**
|
* !#en
|
* Insert an action or tween to this sequence
|
* !#zh
|
* 插入一个 action 或者 tween 到队列中
|
* @method then
|
* @param {Action|Tween} other
|
*/
|
Tween.prototype.then = function (other) {
|
if (other instanceof cc.Action) {
|
this._actions.push(other.clone());
|
}
|
else {
|
let actions = other._actions;
|
for (let i = 0; i < actions.length; i++) {
|
this._actions.push(actions[i].clone());
|
}
|
}
|
return this;
|
};
|
|
/**
|
* !#en
|
* Set tween target
|
* !#zh
|
* 设置 tween 的 target
|
* @method target
|
*/
|
Tween.prototype.target = function (target) {
|
this._target = target;
|
return this;
|
};
|
|
/**
|
* !#en
|
* Start this tween
|
* !#zh
|
* 运行当前 tween
|
* @method start
|
*/
|
Tween.prototype.start = function () {
|
if (!this._target) {
|
cc.warn('Please set target to tween first');
|
return this;
|
}
|
if (!this._finalAction) {
|
this._finalAction = this._get();
|
}
|
cc.director.getActionManager().addAction(this._finalAction, this._target, false);
|
return this;
|
};
|
|
/**
|
* !#en
|
* Stop this tween
|
* !#zh
|
* 停止当前 tween
|
* @method stop
|
*/
|
Tween.prototype.stop = function () {
|
if (this._finalAction) {
|
cc.director.getActionManager().removeAction(this._finalAction);
|
}
|
return this;
|
};
|
|
|
|
/**
|
* !#en
|
* Clone a tween
|
* !#zh
|
* 克隆当前 tween
|
* @method clone
|
* @param {Object} [target]
|
*/
|
Tween.prototype.clone = function (target) {
|
let action = this._get();
|
return cc.tween(target).then(action.clone());
|
};
|
|
/**
|
* !#en
|
* Get an union action from current sequence
|
* !#zh
|
* 从当前队列中获取一个整合的 action
|
*/
|
Tween.prototype._get = function () {
|
let actions = this._actions;
|
|
if (actions.length === 1) {
|
actions = actions[0];
|
}
|
else {
|
actions = cc.sequence(actions);
|
}
|
|
return actions;
|
};
|
|
|
let actions = {
|
/**
|
* !#en
|
* Add an action which calculate with absolute value
|
* !#zh
|
* 添加一个对属性进行绝对值计算的 action
|
* @method to
|
* @param {Number} duration
|
* @param {Object} props - {scale: 2, position: cc.v3(100, 100, 100)}
|
* @param {Object} opts
|
* @param {Function} opts.progress
|
* @param {Function|String} opts.easing
|
*/
|
to (duration, props, opts) {
|
opts = opts || Object.create(null);
|
opts.relative = false;
|
return new TweenAction(duration, props, opts);
|
},
|
|
/**
|
* !#en
|
* Add an action which calculate with relative value
|
* !#zh
|
* 添加一个对属性进行相对值计算的 action
|
* @method by
|
* @param {Number} duration
|
* @param {Object} props - {scale: 2, position: cc.v3(100, 100, 100)}
|
* @param {Object} opts
|
* @param {Function} opts.progress
|
* @param {Function|String} opts.easing
|
*/
|
by (duration, props, opts) {
|
opts = opts || Object.create(null);
|
opts.relative = true;
|
return new TweenAction(duration, props, opts);
|
},
|
|
/**
|
* !#en
|
* Add an delay action
|
* !#zh
|
* 添加一个延时 action
|
* @method delay
|
* @param {Number} duration
|
*/
|
delay: cc.delayTime,
|
/**
|
* !#en
|
* Add an callback action
|
* !#zh
|
* 添加一个回调 action
|
* @method call
|
* @param {Function} callback
|
*/
|
call: cc.callFunc,
|
/**
|
* !#en
|
* Add an hide action
|
* !#zh
|
* 添加一个隐藏 action
|
* @method hide
|
*/
|
hide: cc.hide,
|
/**
|
* !#en
|
* Add an show action
|
* !#zh
|
* 添加一个显示 action
|
* @method show
|
*/
|
show: cc.show,
|
/**
|
* !#en
|
* Add an removeSelf action
|
* !#zh
|
* 添加一个移除自己 action
|
* @method removeSelf
|
*/
|
removeSelf: cc.removeSelf,
|
/**
|
* !#en
|
* Add an sequence action
|
* !#zh
|
* 添加一个队列 action
|
* @method sequence
|
* @param {[Action]} actions
|
*/
|
sequence: cc.sequence,
|
};
|
|
// these action should integrate before actions to a sequence action as their parameters
|
let otherActions = {
|
/**
|
* !#en
|
* Add an repeat action.
|
* This action will integrate before actions to a sequence action as their parameters.
|
* !#zh
|
* 添加一个重复 action,这个 action 会将之前的 action 整合成一个 sequence action 作为他的参数。
|
* @method repeat
|
* @param {Number} repeatTimes
|
*/
|
repeat: cc.repeat,
|
/**
|
* !#en
|
* Add an repeat forever action
|
* This action will integrate before actions to a sequence action as their parameters.
|
* !#zh
|
* 添加一个永久重复 action,这个 action 会将之前的 action 整合成一个 sequence action 作为他的参数。
|
* @method repeatForever
|
*/
|
repeatForever: cc.repeatForever,
|
/**
|
* !#en
|
* Add an reverse time action.
|
* This action will integrate before actions to a sequence action as their parameters.
|
* !#zh
|
* 添加一个倒置时间 action,这个 action 会将之前的 action 整合成一个 sequence action 作为他的参数。
|
* @method reverseTime
|
*/
|
reverseTime: cc.reverseTime,
|
};
|
|
let keys = Object.keys(actions);
|
for (let i = 0; i < keys.length; i++) {
|
let key = keys[i];
|
Tween.prototype[key] = function () {
|
let action = actions[key].apply(actions, arguments);
|
this._actions.push(action);
|
this._finalAction = null;
|
return this;
|
};
|
}
|
|
keys = Object.keys(otherActions);
|
for (let i = 0; i < keys.length; i++) {
|
let key = keys[i];
|
Tween.prototype[key] = function () {
|
let args = [];
|
for (let l = arguments.length, i = 0; i < l; i++) {
|
args[i] = arguments[i];
|
}
|
|
let action = arguments[0];
|
if (!(action instanceof cc.Action)) {
|
action = this._get();
|
}
|
action = otherActions[key].apply(otherActions, [action].concat(args));
|
this._actions.length = 0;
|
this._actions.push(action);
|
this._finalAction = null;
|
return this;
|
};
|
}
|
|
/**
|
* @method tween
|
* @param {Object} [target] - the target to animate
|
* @return {Tween}
|
*/
|
cc.tween = function (target) {
|
return new Tween(target);
|
};
|
|
cc.Tween = Tween;
|