Cocos 动效

在 Cocos 给节点做动画效果,有很多系统提供的实现方式:

1、动画系统 Animation
2、动作系统 action
3、缓动系统 cc.tween

动画系统 Animation 可以在 CocosCreator 手动编辑生成,不用写代码,但是只适用于制作一些不太复杂的场景,而且,如果UI效果要大调,那推倒重做可能好过修修补补,在原基础上调整效果,有时特别苦逼;动作系统 action 就能用代码的方式做动效,调起来也方便,还可以复用,但是 action 代码写起来一大堆,代码多了也不好理解,不好维护;缓动系统 cc.tween 在动作系统 action 的基础上做了一层 API 封装,提供了链式创建的方法,所以 cc.tween 会比 cc.Action 更加简洁易用,cc.tween 可以对任何对象进行操作,并且可以对对象的任意属性进行缓动。本文就是基于 Cocos 官网文档所作的缓动系统 cc.tween 的使用记录。

一、基本使用

1、链式 API

cc.tween 的每一个 API 都会在内部生成一个 action,并将这个 action 添加到内部队列中,在 API 调用完后会再返回自身实例,这样就可以通过链式调用的方式来组织代码。cc.tween 在调用 start 时会将之前生成的 action 队列重新组合生成一个 cc.sequence 队列,所以 cc.tween 的链式结构是依次执行每一个 API 的,也就是会执行完一个 API 再执行下一个 API。

cc.tween(this.node)
    // 0s 时,node 的 scale 还是 1
    .to(1, { scale: 2 })
    // 1s 时,执行完第一个 action,scale 为 2
    .to(1, { scale: 3 })
    // 2s 时,执行完第二个 action,scale 为 3
    .start()
    // 调用 start 开始执行 cc.tween

2、设置缓动属性

cc.tween 提供了两个设置属性的 API:
to:对属性进行绝对值计算,最终的运行结果即是设置的属性值
by:对属性进行相对值计算,最终的运行结果是设置的属性值加上开始运行时节点的属性值

cc.tween(node)
  .to(1, {scale: 2})      // node.scale === 2
  .by(1, {scale: 2})      // node.scale === 4 (2+2)
  .by(1, {scale: 1})      // node.scale === 5
  .to(1, {scale: 2})      // node.scale === 2
  .start()

同时执行多个属性

cc.tween(this.node)
    // 同时对 scale, position, rotation 三个属性缓动
    .to(1, { scale: 2, position: cc.v2(100, 100), rotation: 90 })
    .start()

可用属性

opacity、scale、scaleX、scaleY、angle(代替 rotation,为逆时针方向)、position、等等等等

3、回调

cc.tween(this.node)
    .to(2, { angle: -90})
    .to(1, { scale: 2})
    // 当前面的动作都执行完毕后才会调用这个回调函数
    .call(() => { cc.log('This is a callback') })
    .start()

4、延迟执行

cc.tween(this.node)
    // 延迟 1s
    .delay(1)
    .to(1, { scale: 2 })
    // 再延迟 1s
    .delay(1)
    .to(1, { scale: 3 })
    .start()

5、重复执行

repeat / repeatForever 函数会将前一个 action 作为作用对象。但是如果有参数提供了其他的 action 或者 tween,则 repeat / repeatForever 函数会将传入的 action 或者 tween 作为作用对象。

cc.tween(this.node)
    .by(1, { scale: 1 })
    // 对前一个 by 重复执行 10次
    .repeat(10)
    // 最后 node.scale === 11
    .start()

// 也可以这样用
cc.tween(this.node)
    .repeat(10,
        cc.tween().by(1, { scale: 1 })
    )
    .start()

// 一直重复执行下去
cc.tween(this.node)
    .by(1, { scale: 1 })
    .repeatForever()
    .start()

二、中级使用

1、并行执行缓动

cc.tween 在链式执行时是按照 sequence 的方式来执行的,但是在编写复杂缓动的时候可能会需要同时并行执行多个队列,cc.tween 提供了 parallel 接口来满足这个需求。

let t = cc.tween;
t(this.node)
    // 同时执行两个 cc.tween
    .parallel(
        t().to(1, { scale: 2 }),
        t().to(2, { position: cc.v2(100, 100) })
    )
    .call(() => {
        console.log('All tweens finished.')
    })
    .start()

2、easing

你可以使用 easing 来使缓动更生动,cc.tween 针对不同的情况提供了多种使用方式。

// 传入 easing 名字,直接使用内置 easing 函数
cc.tween().to(1, { scale: 2 }, { easing: 'sineOutIn'})

// 使用自定义 easing 函数
cc.tween().to(1, { scale: 2 }, { easing: t => t*t; })

// 只对单个属性使用 easing 函数
// value 必须与 easing 或者 progress 配合使用
cc.tween().to(1, { scale: 2, position: { value: cc.v3(100, 100, 100), easing: 'sineOutIn' } })

Easing 类型说明可参考 API 文档。

三、中高级使用

1、自定义 progress

相对于 easing,自定义 progress 函数可以更自由的控制缓动的过程。

// 对所有属性自定义 progress
cc.tween().to(1, { scale: 2, rotation: 90 }, {
  progress: (start, end, current, ratio) => {
    return start + (end - start) * ratio;
  }
})

// 对单个属性自定义 progress
cc.tween().to(1, {
  scale: 2,
  position: {
    value: cc.v3(),
    progress: (start, end, current, t) => {
      // value 是被操作属性(如此处的 progress)的设定的最终结果值
      // 注意,传入的属性为 cc.Vec3,所以需要使用 Vec3.lerp 进行插值计算
      return start.lerp(end, t, current);
    }
  }
})

例一:果冻动效
有一天,UI 的大兄弟 BIA 给我一段代码,要我实现果冻动效:

freq = 4;
decay = 3;
n = 0;
if (numKeys > 0){
  n = nearestKey(time).index;
  if (key(n).time > time) n--;
}
if (n > 0){
  t = time - key(n).time;
  amp = velocityAtTime(key(n).time - .001);
  w = freq*Math.PI*2;
  value + amp*(Math.sin(t*w)/Math.exp(decay*t)/w);
}else
  value

作为一个程序员,UI 都给代码了,不上不行啊,但是代码拆开看得懂,合起来就没头绪了,明显跟咱的 style 有区别,最后查资料,发现是 AE 导出的代码:

【AE表达式】万能弹性表达式: https://www.jianshu.com/p/1189e7bc8187

一顿阅读理解,提取中心思想后,发现关键只有这一句代码:

value + amp*(Math.sin(t*w)/Math.exp(decay*t)/w);

上呗:

// 果冻动效
let freq = 4;
let decay = 3;
let w = freq * Math.PI * 2;
cc.tween(this.targetNode)
    .repeatForever(
       cc.tween()
            .to(0.4, { scaleX: 1.07, scaleY: 0.93 }, { easing: 'sineOut' })
            .to(0.2, { scaleX: 1, scaleY: 1 }, { easing: 'sineOut' })
            .to(1, {
                scaleX: 1,
                scaleY: {
                    value: 1,
                    progress: (start, end, current, ratio) => {
                        return end + end * (Math.sin(ratio * w) / Math.exp(decay * ratio) / w);
                    }
                }
            })
            .delay(0.66)
    )
   .start();

参考:
1、使用缓动系统:https://docs.cocos.com/creator/manual/zh/scripting/tween.html
2、【AE表达式】万能弹性表达式: https://www.jianshu.com/p/1189e7bc8187

你可能感兴趣的:(Cocos)