mirror of
https://github.com/smallmain/cocos-enhance-kit.git
synced 2024-12-26 11:48:29 +00:00
707 lines
19 KiB
JavaScript
707 lines
19 KiB
JavaScript
import { bezier } from '../animation/bezier';
|
||
|
||
let _tweenID = 0;
|
||
|
||
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') {
|
||
let easingName = opts.easing;
|
||
opts.easing = cc.easing[easingName];
|
||
!opts.easing && cc.warnID(1031, easingName);
|
||
}
|
||
|
||
let relative = this._opts.relative;
|
||
|
||
for (let name in props) {
|
||
let value = props[name];
|
||
|
||
// property may have custom easing or progress function
|
||
let easing, progress;
|
||
if (value.value !== undefined && (value.easing || value.progress)) {
|
||
if (typeof value.easing === 'string') {
|
||
easing = cc.easing[value.easing];
|
||
!easing && cc.warnID(1031, value.easing);
|
||
}
|
||
else {
|
||
easing = value.easing;
|
||
}
|
||
progress = value.progress;
|
||
value = value.value;
|
||
}
|
||
|
||
let isNumber = typeof value === 'number';
|
||
if (!isNumber && (!value.lerp || (relative && !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 = 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;
|
||
}
|
||
|
||
let onUpdate = opts.onUpdate;
|
||
if (onUpdate) {
|
||
onUpdate(target, t)
|
||
}
|
||
},
|
||
|
||
progress (start, end, current, t) {
|
||
if (typeof start === 'number') {
|
||
current = start + (end - start) * t;
|
||
}
|
||
else {
|
||
start.lerp(end, t, current);
|
||
}
|
||
return current;
|
||
}
|
||
});
|
||
|
||
let SetAction = cc.Class({
|
||
name: 'cc.SetAction',
|
||
extends: cc.ActionInstant,
|
||
|
||
ctor (props) {
|
||
this._props = {};
|
||
props !== undefined && this.init(props);
|
||
},
|
||
|
||
init (props) {
|
||
for (let name in props) {
|
||
this._props[name] = props[name];
|
||
}
|
||
return true;
|
||
},
|
||
|
||
update () {
|
||
let props = this._props;
|
||
let target = this.target;
|
||
for (let name in props) {
|
||
target[name] = props[name];
|
||
}
|
||
},
|
||
|
||
clone () {
|
||
var action = new SetAction();
|
||
action.init(this._props);
|
||
return action;
|
||
}
|
||
});
|
||
|
||
|
||
|
||
/**
|
||
* !#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` 混用。
|
||
* - 支持设置 {{#crossLink "Easing"}}{{/crossLink}} 或者 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'})
|
||
* .start(cc.find('Canvas/cocos'));
|
||
* @typescript Tween<T = any>
|
||
*/
|
||
function Tween (target) {
|
||
this._actions = [];
|
||
this._finalAction = null;
|
||
this._target = target;
|
||
this._tag = cc.Action.TAG_INVALID;
|
||
}
|
||
|
||
/**
|
||
* @method constructor
|
||
* @param {Object} [target]
|
||
*/
|
||
|
||
/**
|
||
* !#en Stop all tweens
|
||
* !#zh 停止所有缓动
|
||
* @method stopAll
|
||
* @static
|
||
*/
|
||
Tween.stopAll = function () {
|
||
cc.director.getActionManager().removeAllActions();
|
||
}
|
||
/**
|
||
* !#en Stop all tweens by tag
|
||
* !#zh 停止所有指定标签的缓动
|
||
* @method stopAllByTag
|
||
* @static
|
||
* @param {number} tag
|
||
*/
|
||
Tween.stopAllByTag = function (tag) {
|
||
cc.director.getActionManager().removeAllActionsByTag(tag);
|
||
}
|
||
/**
|
||
* !#en Stop all tweens by target
|
||
* !#zh 停止所有指定对象的缓动
|
||
* @method stopAllByTarget
|
||
* @static
|
||
* @param {Object} target
|
||
*/
|
||
Tween.stopAllByTarget = function (target) {
|
||
cc.director.getActionManager().removeAllActionsFromTarget(target);
|
||
}
|
||
|
||
/**
|
||
* !#en
|
||
* Insert an action or tween to this sequence
|
||
* !#zh
|
||
* 插入一个 action 或者 tween 到队列中
|
||
* @method then
|
||
* @param {Action|Tween} other
|
||
* @return {Tween}
|
||
* @typescript then(other: Action|Tween<T>): Tween<T>
|
||
*/
|
||
Tween.prototype.then = function (other) {
|
||
if (other instanceof cc.Action) {
|
||
this._actions.push(other.clone());
|
||
}
|
||
else {
|
||
this._actions.push(other._union());
|
||
}
|
||
return this;
|
||
};
|
||
|
||
|
||
/**
|
||
* !#en
|
||
* Set tween target
|
||
* !#zh
|
||
* 设置 tween 的 target
|
||
* @method target
|
||
* @param {Object} target
|
||
* @return {Tween}
|
||
* @typescript target(target: any): Tween<T>
|
||
*/
|
||
Tween.prototype.target = function (target) {
|
||
this._target = target;
|
||
return this;
|
||
};
|
||
|
||
/**
|
||
* !#en
|
||
* Start this tween
|
||
* !#zh
|
||
* 运行当前 tween
|
||
* @method start
|
||
* @return {Tween}
|
||
* @typescript start(): Tween<T>
|
||
*/
|
||
Tween.prototype.start = function () {
|
||
let target = this._target;
|
||
if (!target) {
|
||
cc.warn('Please set target to tween first');
|
||
return this;
|
||
}
|
||
if (target instanceof cc.Object && !target.isValid) {
|
||
return;
|
||
}
|
||
|
||
if (this._finalAction) {
|
||
cc.director.getActionManager().removeAction(this._finalAction);
|
||
}
|
||
this._finalAction = this._union();
|
||
|
||
if (target._id === undefined) {
|
||
target._id = ++_tweenID;
|
||
}
|
||
|
||
this._finalAction.setTag(this._tag);
|
||
cc.director.getActionManager().addAction(this._finalAction, target, false);
|
||
return this;
|
||
};
|
||
|
||
/**
|
||
* !#en
|
||
* Stop this tween
|
||
* !#zh
|
||
* 停止当前 tween
|
||
* @method stop
|
||
* @return {Tween}
|
||
* @typescript stop(): Tween<T>
|
||
*/
|
||
Tween.prototype.stop = function () {
|
||
if (this._finalAction) {
|
||
cc.director.getActionManager().removeAction(this._finalAction);
|
||
}
|
||
return this;
|
||
};
|
||
|
||
|
||
/**
|
||
* !#en Sets tween tag
|
||
* !#zh 设置缓动的标签
|
||
* @method tag
|
||
* @param {number} tag
|
||
* @return {Tween}
|
||
* @typescript tag(tag: number): Tween<T>
|
||
*/
|
||
Tween.prototype.tag = function (tag) {
|
||
this._tag = tag;
|
||
return this;
|
||
};
|
||
|
||
|
||
/**
|
||
* !#en
|
||
* Clone a tween
|
||
* !#zh
|
||
* 克隆当前 tween
|
||
* @method clone
|
||
* @param {Object} [target]
|
||
* @return {Tween}
|
||
* @typescript clone(target?: any): Tween<T>
|
||
*/
|
||
Tween.prototype.clone = function (target) {
|
||
let action = this._union();
|
||
return cc.tween(target).then(action.clone());
|
||
};
|
||
|
||
/**
|
||
* !#en
|
||
* Integrate all previous actions to an action.
|
||
* !#zh
|
||
* 将之前所有的 action 整合为一个 action。
|
||
* @method union
|
||
* @return {Tween}
|
||
* @typescritp union(): Tween<T>
|
||
*/
|
||
Tween.prototype.union = function () {
|
||
let action = this._union();
|
||
this._actions.length = 0;
|
||
this._actions.push(action);
|
||
return this;
|
||
};
|
||
|
||
Tween.prototype._union = function () {
|
||
let actions = this._actions;
|
||
|
||
if (actions.length === 1) {
|
||
actions = actions[0];
|
||
}
|
||
else {
|
||
actions = cc.sequence(actions);
|
||
}
|
||
|
||
return actions;
|
||
};
|
||
|
||
Object.assign(Tween.prototype, {
|
||
/**
|
||
* !#en Sets target's position property according to the bezier curve.
|
||
* !#zh 按照贝塞尔路径设置目标的 position 属性。
|
||
* @method bezierTo
|
||
* @param {number} duration
|
||
* @param {cc.Vec2} c1
|
||
* @param {cc.Vec2} c2
|
||
* @param {cc.Vec2} to
|
||
* @return {Tween}
|
||
* @typescript bezierTo(duration: number, c1: Vec2, c2: Vec2, to: Vec2): Tween<T>
|
||
*/
|
||
bezierTo (duration, c1, c2, to, opts) {
|
||
let c0x = c1.x, c0y = c1.y,
|
||
c1x = c2.x, c1y = c2.y;
|
||
opts = opts || Object.create(null);
|
||
opts.progress = function (start, end, current, t) {
|
||
current.x = bezier(start.x, c0x, c1x, end.x, t);
|
||
current.y = bezier(start.y, c0y, c1y, end.y, t);
|
||
return current;
|
||
}
|
||
return this.to(duration, { position: to }, opts);
|
||
},
|
||
|
||
/**
|
||
* !#en Sets target's position property according to the bezier curve.
|
||
* !#zh 按照贝塞尔路径设置目标的 position 属性。
|
||
* @method bezierBy
|
||
* @param {number} duration
|
||
* @param {cc.Vec2} c1
|
||
* @param {cc.Vec2} c2
|
||
* @param {cc.Vec2} to
|
||
* @return {Tween}
|
||
* @typescript bezierBy(duration: number, c1: Vec2, c2: Vec2, to: Vec2): Tween<T>
|
||
*/
|
||
bezierBy (duration, c1, c2, to, opts) {
|
||
let c0x = c1.x, c0y = c1.y,
|
||
c1x = c2.x, c1y = c2.y;
|
||
opts = opts || Object.create(null);
|
||
opts.progress = function (start, end, current, t) {
|
||
let sx = start.x, sy = start.y;
|
||
current.x = bezier(sx, c0x + sx, c1x + sx, end.x, t);
|
||
current.y = bezier(sy, c0y + sy, c1y + sy, end.y, t);
|
||
return current;
|
||
}
|
||
return this.by(duration, { position: to }, opts);
|
||
},
|
||
|
||
/**
|
||
* !#en Flips target's scaleX
|
||
* !#zh 翻转目标的 scaleX 属性
|
||
* @method flipX
|
||
* @return {Tween}
|
||
* @typescript flipX(): Tween<T>
|
||
*/
|
||
flipX () {
|
||
return this.call(() => { this._target.scaleX *= -1; }, this);
|
||
|
||
},
|
||
/**
|
||
* !#en Flips target's scaleY
|
||
* !#zh 翻转目标的 scaleY 属性
|
||
* @method flipY
|
||
* @return {Tween}
|
||
* @typescript flipY(): Tween<T>
|
||
*/
|
||
flipY () {
|
||
return this.call(() => { this._target.scaleY *= -1; }, this);
|
||
},
|
||
|
||
/**
|
||
* !#en Blinks target by set target's opacity property
|
||
* !#zh 通过设置目标的 opacity 属性达到闪烁效果
|
||
* @method blink
|
||
* @param {number} duration
|
||
* @param {number} times
|
||
* @param {Object} [opts]
|
||
* @param {Function} [opts.progress]
|
||
* @param {Function|String} [opts.easing]
|
||
* @return {Tween}
|
||
* @typescript blink(duration: number, times: number, opts?: {progress?: Function; easing?: Function|string; }): Tween<T>
|
||
*/
|
||
blink (duration, times, opts) {
|
||
var slice = 1.0 / times;
|
||
opts = opts || Object.create(null);
|
||
opts.progress = function (start, end, current, t) {
|
||
if (t >= 1) {
|
||
return start;
|
||
}
|
||
else {
|
||
var m = t % slice;
|
||
return (m > (slice / 2)) ? 255 : 0;
|
||
}
|
||
};
|
||
return this.to(duration, { opacity: 1 }, opts);
|
||
},
|
||
})
|
||
|
||
let tmp_args = [];
|
||
|
||
function wrapAction (action) {
|
||
return function () {
|
||
tmp_args.length = 0;
|
||
for (let l = arguments.length, i = 0; i < l; i++) {
|
||
let arg = tmp_args[i] = arguments[i];
|
||
if (arg instanceof Tween) {
|
||
tmp_args[i] = arg._union();
|
||
}
|
||
}
|
||
|
||
return action.apply(this, tmp_args);
|
||
};
|
||
}
|
||
|
||
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]
|
||
* @return {Tween}
|
||
* @typescript
|
||
* to<OPTS extends Partial<{ progress: Function, easing: Function | String, onUpdate: Function }>>(duration: number, props: ConstructorType<T>, opts?: OPTS): Tween<T>
|
||
*/
|
||
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]
|
||
* @return {Tween}
|
||
* @typescript
|
||
* by<OPTS extends Partial<{ progress: Function, easing: Function | String, onUpdate: Function }>>(duration: number, props: ConstructorType<T>, opts?: OPTS): Tween<T>
|
||
*/
|
||
by (duration, props, opts) {
|
||
opts = opts || Object.create(null);
|
||
opts.relative = true;
|
||
return new TweenAction(duration, props, opts);
|
||
},
|
||
|
||
/**
|
||
* !#en
|
||
* Directly set target properties
|
||
* !#zh
|
||
* 直接设置 target 的属性
|
||
* @method set
|
||
* @param {Object} props
|
||
* @return {Tween}
|
||
* @typescript
|
||
* set (props: ConstructorType<T>) : Tween<T>
|
||
*/
|
||
set (props) {
|
||
return new SetAction(props);
|
||
},
|
||
|
||
/**
|
||
* !#en
|
||
* Add an delay action
|
||
* !#zh
|
||
* 添加一个延时 action
|
||
* @method delay
|
||
* @param {Number} duration
|
||
* @return {Tween}
|
||
* @typescript delay(duration: number): Tween<T>
|
||
*/
|
||
delay: cc.delayTime,
|
||
/**
|
||
* !#en
|
||
* Add an callback action
|
||
* !#zh
|
||
* 添加一个回调 action
|
||
* @method call
|
||
* @param {Function} callback
|
||
* @param {object} [selectTarget]
|
||
* @return {Tween}
|
||
* @typescript call(callback: Function, selectTarget?: object): Tween<T>
|
||
*/
|
||
call: cc.callFunc,
|
||
/**
|
||
* !#en
|
||
* Add an hide action
|
||
* !#zh
|
||
* 添加一个隐藏 action
|
||
* @method hide
|
||
* @return {Tween}
|
||
* @typescript hide(): Tween<T>
|
||
*/
|
||
hide: cc.hide,
|
||
/**
|
||
* !#en
|
||
* Add an show action
|
||
* !#zh
|
||
* 添加一个显示 action
|
||
* @method show
|
||
* @return {Tween}
|
||
* @typescript show(): Tween<T>
|
||
*/
|
||
show: cc.show,
|
||
/**
|
||
* !#en
|
||
* Add an removeSelf action
|
||
* !#zh
|
||
* 添加一个移除自己 action
|
||
* @method removeSelf
|
||
* @return {Tween}
|
||
* @typescript removeSelf(): Tween<T>
|
||
*/
|
||
removeSelf: cc.removeSelf,
|
||
/**
|
||
* !#en
|
||
* Add an sequence action
|
||
* !#zh
|
||
* 添加一个队列 action
|
||
* @method sequence
|
||
* @param {Action|Tween} action
|
||
* @param {Action|Tween} ...actions
|
||
* @return {Tween}
|
||
* @typescript sequence(action: Action|Tween<T>, ...actions: (Action|Tween<T>)[]): Tween<T>
|
||
*/
|
||
sequence: wrapAction(cc.sequence),
|
||
/**
|
||
* !#en
|
||
* Add an parallel action
|
||
* !#zh
|
||
* 添加一个并行 action
|
||
* @method parallel
|
||
* @param {Action|Tween} action
|
||
* @param {Action|Tween} ...actions
|
||
* @return {Tween}
|
||
* @typescript parallel(action: Action|Tween<T>, ...actions: (Action|Tween<T>)[]): Tween<T>
|
||
*/
|
||
parallel: wrapAction(cc.spawn)
|
||
};
|
||
|
||
// these action will use previous action as their parameters
|
||
let previousAsInputActions = {
|
||
/**
|
||
* !#en
|
||
* Add an repeat action. This action will integrate before actions to a sequence action as their parameters.
|
||
* !#zh
|
||
* 添加一个重复 action,这个 action 会将前一个动作作为他的参数。
|
||
* @method repeat
|
||
* @param {Number} repeatTimes
|
||
* @param {Action | Tween} [action]
|
||
* @return {Tween}
|
||
* @typescript repeat(repeatTimes: number, action?: Action|Tween<T>): Tween<T>
|
||
*/
|
||
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 会将前一个动作作为他的参数。
|
||
* @method repeatForever
|
||
* @param {Action | Tween} [action]
|
||
* @return {Tween}
|
||
* @typescript repeatForever(action?: Action|Tween<T>): Tween<T>
|
||
*/
|
||
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 会将前一个动作作为他的参数。
|
||
* @method reverseTime
|
||
* @param {Action | Tween} [action]
|
||
* @return {Tween}
|
||
* @typescript reverseTime(action?: Action|Tween<T>): Tween<T>
|
||
*/
|
||
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(this, arguments);
|
||
this._actions.push(action);
|
||
return this;
|
||
};
|
||
}
|
||
|
||
keys = Object.keys(previousAsInputActions);
|
||
for (let i = 0; i < keys.length; i++) {
|
||
let key = keys[i];
|
||
Tween.prototype[key] = function () {
|
||
|
||
let actions = this._actions;
|
||
let action = arguments[arguments.length - 1];
|
||
let length = arguments.length - 1;
|
||
|
||
if (action instanceof cc.Tween) {
|
||
action = action._union();
|
||
}
|
||
else if (!(action instanceof cc.Action)) {
|
||
action = actions[actions.length - 1];
|
||
actions.length -= 1;
|
||
length += 1;
|
||
}
|
||
|
||
let args = [action];
|
||
for (let i = 0; i < length; i++) {
|
||
args.push(arguments[i]);
|
||
}
|
||
|
||
action = previousAsInputActions[key].apply(this, args);
|
||
actions.push(action);
|
||
|
||
return this;
|
||
};
|
||
}
|
||
|
||
/**
|
||
* @module cc
|
||
*/
|
||
|
||
/**
|
||
* @method tween
|
||
* @param {Object} [target] - the target to animate
|
||
* @return {Tween}
|
||
* @typescript
|
||
* tween<T> (target?: T) : Tween<T>
|
||
*/
|
||
cc.tween = function (target) {
|
||
return new Tween(target);
|
||
};
|
||
|
||
cc.Tween = Tween;
|
||
|