2022-06-25 00:23:03 +08:00
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" } } { { / c r o s s L i n k } } 或 者 p r o g r e s s 函 数 。
* @ 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 到队列中
2022-10-01 17:51:47 +08:00
* @ method then
2022-06-25 00:23:03 +08:00
* @ 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 ) ;
2022-10-01 17:51:47 +08:00
this . _finalAction = null ;
2022-06-25 00:23:03 +08:00
}
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 ) ;
2022-10-01 17:51:47 +08:00
2022-06-25 00:23:03 +08:00
} ,
/ * *
* ! # 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 ;
2022-10-01 17:51:47 +08:00