缓动示例

    通过 方法或使用 new Tween<T>(target: T) 都可以构造缓动。

    链式 API

    大部分和动作相关的接口都会返回 this 或者一个新的 Tween 对象,因此可以方便的使用链式调用来进行组合:

    这里演示了如何使用一个 to 类型的缓动绑定节点的位置信息并将其位置沿 Y 轴偏移 10 个单位:

    1. let tweenDuration : number = 1.0; // 缓动的时长
    2. tween(this.node.position)
    3. .to( tweenDuration, new Vec3(0, 10, 0), { // to 接口表示节点的绝对值
    4. onUpdate : (target:Vec3, ratio:number)=>{ // 实现 ITweenOption 的 onUpdate 回调,接受当前缓动的进度
    5. this.node.position = target; // 将缓动系统计算出的结果赋予 node 的位置
    6. }
    7. }).start(); // 调用 start 方法,开启缓动

    绑定不同对象

    开发中使用 Node 作为绑定目标的情景会更多一些,代码示例如下:

    1. let quat : Quat = new Quat();
    2. Quat.fromEuler(quat, 0, 90, 0);
    3. tween(this.node)
    4. .to(tweenDuration, {
    5. position: new Vec3(0, 10, 0), // 位置缓动
    6. scale: new Vec3(1.2, 3, 1), // 缩放缓动
    7. rotation:quat } // 旋转缓动
    8. )
    9. .start(); // 调用 start 方法,开启缓动

    实际上缓动可以绑定任意对象,代码示例如下:

    1. class BindTarget{
    2. color : Color
    3. }
    4. let sprite : Sprite = this.node.getComponent(Sprite) ;
    5. let bindTarget : BindTarget = new BindTarget();
    6. bindTarget.color = Color.BLACK;
    7. tween(bindTarget)
    8. .by( 1.0, { color: Color.RED }, {
    9. onUpdate(tar:BindTarget){
    10. sprite.color = tar.color; // 设置精灵的为 BindTarget 内的颜色
    11. }
    12. .start()

    通常来说,一个缓动可由一个或多个 动作 组成,Tween 维护了一个由多个 动作 组成的数据结构用于管理当前缓动内的所有动作。

    下面代码演示了将物体的位置沿 Y 轴移动 10 个单位后,沿 -Y 轴移动 10 个单位。

    1. let tweenDuration : number = 1.0;
    2. tween(this.node.position)
    3. .to( tweenDuration, new Vec3(0, 10, 0), {
    4. onUpdate : (target:Vec3, ratio:number)=>{
    5. this.node.position = target;
    6. }
    7. })
    8. .to( tweenDuration, new Vec3(0, -10, 0), {
    9. onUpdate : (target:Vec3, ratio:number)=>{
    10. this.node.position = target;
    11. }
    12. }) // 此时 Tween 内的动作数量为 2

    多个缓动也可使用 unionsquenceparallel 接口来组织。通过提前创建好一些固定的缓动,并使用 unionsquence、 来组合他们从而减少代码的编写。

    整合多个缓动

    union 方法会将当前所有的动作合并为一整个,代码示例如下:

    1. let tweenDuration : number = 1.0;
    2. tween(this.node)
    3. .to(tweenDuration, { position:new Vec3(0, 10, 0) }) // 这里以 node 为缓动的目标
    4. .to(tweenDuration, { position:new Vec3(0, -10, 0) }) // 此时 Tween 内的动作数量为 2
    5. .union() // 这里会将上述的两个缓动整合成一个,此时 Tween 内的动作数量为 1
    6. .start(); // 调用 start 方法,开启缓动

    缓动队列

    sequence 会将传入的缓动转化为队列形式并加入到当前的缓动内,代码示例如下:

    1. let tweenDuration: number = 1.0;
    2. let t1 = tween(this.node)
    3. .to(tweenDuration, { position: new Vec3(0, 10, 0) })
    4. let t2 = tween(this.node)
    5. .to(tweenDuration, { position: new Vec3(0, -10, 0) })
    6. tween(this.node).sequence(t1, t2).start(); // 将 t1 和 t2 两个缓动加入到新的缓动队列内

    插入缓动

    then 接口允许传入新的缓动,并将该缓动整合后添加到当前缓动的动作内,代码示例如下:

    1. let tweenAfter = tween(this.node)
    2. .to(1.0, { position: new Vec3(0, -10, 0) })
    3. tween(this.node)
    4. .by(1.0, { position: new Vec3(0, 10, 0) })
    5. .then(tweenAfter)
    6. .start();

    delay 会在 当前 的动作 添加一个延时。

    注意在下列代码示例中,delay 位置不同会造成完全不同的结果:

    • 延迟 1 秒后,开始进行运动,并连续运动两次。

      1. let tweenDuration: number = 1.0;
      2. tween(this.node)
      3. .delay(1.0)
      4. .to(tweenDuration, { position: new Vec3(0, 10, 0) })
      5. .to(tweenDuration, { position: new Vec3(0, -10, 0) })
      6. .start()
    • 在第一次运动后,会延迟 1 秒再做第二次运动。

      1. let tweenDuration: number = 1.0;
      2. tween(this.node)
      3. .to(tweenDuration, { position: new Vec3(0, 10, 0) })
      4. .delay(1.0)
      5. .to(tweenDuration, { position: new Vec3(0, -10, 0) })
      6. .start()

    重复执行

    接口 repeat 可以为缓动添加一个重复次数,若 embedTween 参数为空,则会使用当前缓动的最后一个动作作为参数。

    这意味着,如果当前缓动由多个缓动组成,则只会重复 最后一个,请注意下面的示例:

    1. let tweenDuration: number = 1.0;
    2. .to(tweenDuration, { position: new Vec3(0, 10, 0) })
    3. .by(tweenDuration, { position: new Vec3(0, -10, 0) })
    4. .repeat(3) // 注意这里会重复 by 这个缓动 3 次
    5. .start()

    若第二个参数 embedTween 不为空,则会重复嵌入的缓动,代码示例如下:

    1. let tweenDuration: number = 1.0;
    2. let embedTween = tween(this.node)
    3. .by(tweenDuration, { position: new Vec3(0, -10, 0) })
    4. tween(this.node)
    5. .repeat(3, embedTween) // 这里会重复 embedTween
    6. .start()

    repeatForever 接口和 repeat 类似,但是会变为永久重复。

    节点相关的缓动

    节点相关的方法只适用于 targetNode 的情况。

    显示和隐藏节点

    showhide 接口可以控制绑定节点的显示和隐藏,下面示例中,节点会被隐藏并在延迟 1 秒后显示。

    1. tween(this.node)
    2. .hide()
    3. .delay(1.0)
    4. .show()
    5. .start();

    删除节点

    在下面的示例中,节点会在延迟 1 秒后从场景内删除。

    call 接口允许给缓动添加一个回调动作,该接口在处理某些异步逻辑时非常有用,示例如下:

    1. tween(this.node)
    2. .to(1.0, { position: new Vec3(0, 10, 0)})
    3. // to 动作完成后会调用该方法
    4. .call( ()=>{
    5. console.log("call");
    6. })
    7. .start()

    设置目标属性

    通过 set 可以设置目标的属性。下面的示例会在延迟 1 秒后将节点设置在 [0, 100, 0] 的位置。

    1. tween(this.node)
    2. .delay(1.0)
    3. .set({ position: new Vec3(0, 100, 0) })
    4. .start();

    也可以同时设置多个不同的属性,代码示例如下:

    1. tween(this.node)
    2. // 同时设置节点的位置,缩放和旋转
    3. .set({ position: new Vec3(0, 100, 0), scale: new Vec3(2, 2, 2), rotation: Quat.IDENTITY } )
    4. .start();

    复制缓动

    clone 方法可将当前的缓动复制到目标参数上,注意在复制时源缓动和目前缓动的绑定对象要类型一致,即 new Tween<T>(target: T) 中的 T 需要类型一致。代码示例如下:

    1. let srcTween = tween(this.node).delay(1.0).by(1.0, { position: new Vec3(0, 10, 0) })
    2. // 将 ‘srcTween’ 复制到名为 Cone 的节点上
    3. srcTween.clone(find("Cone")).start();

    销毁

    当缓动目标为 Node 时,将会监听其销毁事件进行缓动的自动销毁,调用 target 方法也会自动更新监听。

    手动销毁

    大部分缓动在最后一个动作完毕后,都会对自身进行销毁,但是对于未能正确销毁的缓动, 如 repeatForever 在切换场景后,会一直驻留在内存中。需要手动调用销毁接口进行销毁。

    如果要停止并销毁缓动,有下列的方法:

    • 成员 stop 接口,销毁该缓动,代码示例如下:

      1. let t = tween(this.node.position)
      2. .to( 1.0, new Vec3(0, 10, 0), {
      3. onUpdate : (target:Vec3, ratio:number)=>{
      4. this.node.position = target;
      5. }
      6. })
      7. t.stop();
    • 使用静态接口 stopAllstopAllByTagstopAllByTarget 销毁所有或特定缓动,代码示例如下:

      1. Tween.stopAll() // 销毁所有缓动
      2. Tween.stopAllByTag(0); // 销毁所有以 0 为标签的缓动
      3. Tween.stopAllByTarget(this.node); // 销毁该节点上的所有缓动

    注意:切换场景时记得停止相应的缓动。