• 在 Cocos Creator 中使用缓动系统(cc.tween)
    • 缓动系统(cc.tween)介绍
    • 链式 API
    • 设置缓动属性
    • 支持缓动任意对象的任意属性
    • 同时执行多个属性
    • easing
    • 自定义 progress
    • 复制缓动
    • 插入其他的缓动到队列中
    • 并行执行缓动
    • 回调
    • 重复执行
    • 延迟执行

    在 Cocos Creator 中使用缓动系统(cc.tween)

    文:youyou、薰依

    缓动系统(cc.tween)介绍

    Cocos Creator 在 v2.0.9 提供了一套新的 API —— cc.tweencc.tween 能够对对象的任意属性进行缓动,功能类似于 cc.Action(动作系统)。但是 cc.tween 会比 cc.Action 更加简洁易用,因为 cc.tween 提供了链式创建的方法,可以对任何对象进行操作,并且可以对对象的任意属性进行缓动。

    动作系统 是从 Cocos2d-x 迁移到 Cocos Creator 的,提供的 API 比较繁琐,只支持在节点属性上使用,并且如果要支持新的属性就需要再添加一个新的动作。为了提供更好的 API,cc.tween 在 动作系统 的基础上做了一层 API 封装。下面是 cc.Actioncc.tween 在使用上的对比:

    • cc.Action
    1. this.node.runAction(
    2. cc.sequence(
    3. cc.spawn(
    4. cc.moveTo(1, 100, 100),
    5. cc.rotateTo(1, 360),
    6. ),
    7. cc.scale(1, 2)
    8. )
    9. )
    • cc.tween
    1. cc.tween(this.node)
    2. .to(1, { position: cc.v2(100, 100), rotation: 360 })
    3. .to(1, { scale: 2 })
    4. .start()

    链式 API

    cc.tween 的每一个 API 都会在内部生成一个 action,并将这个 action 添加到内部队列中,在 API 调用完后会再返回自身实例,这样就可以通过链式调用的方式来组织代码。

    cc.tween 在调用 start 时会将之前生成的 action 队列重新组合生成一个 cc.sequence 队列,所以 cc.tween 的链式结构是依次执行每一个 API 的,也就是会执行完一个 API 再执行下一个 API。

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

    设置缓动属性

    cc.tween 提供了两个设置属性的 API:

    • to:对属性进行绝对值计算,最终的运行结果即是设置的属性值
    • by:对属性进行相对值计算,最终的运行结果是设置的属性值加上开始运行时节点的属性值
    1. cc.tween(node)
    2. .to(1, {scale: 2}) // node.scale === 2
    3. .by(1, {scale: 2}) // node.scale === 4 (2+2)
    4. .by(1, {scale: 1}) // node.scale === 5
    5. .to(1, {scale: 2}) // node.scale === 2
    6. .start()

    支持缓动任意对象的任意属性

    1. let obj = { a: 0 }
    2. cc.tween(obj)
    3. .to(1, { a: 100 })
    4. .start()

    同时执行多个属性

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

    easing

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

    1. // 传入 easing 名字,直接使用内置 easing 函数
    2. cc.tween().to(1, { scale: 2 }, { easing: 'sineOutIn'})
    3. // 使用自定义 easing 函数
    4. cc.tween().to(1, { scale: 2 }, { easing: t => t*t; })
    5. // 只对单个属性使用 easing 函数
    6. // value 必须与 easing 或者 progress 配合使用
    7. cc.tween().to(1, { scale: 2, position: { value: cc.v3(100, 100, 100), easing: 'sineOutIn' } })

    自定义 progress

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

    1. // 对所有属性自定义 progress
    2. cc.tween().to(1, { scale: 2, rotation: 90 }, {
    3. progress: (start, end, current, ratio) => {
    4. return start + (end - start) * ratio;
    5. }
    6. })
    7. // 对单个属性自定义 progress
    8. cc.tween().to(1, {
    9. scale: 2,
    10. position: {
    11. value: cc.v3(),
    12. progress: (start, end, current, t) => {
    13. // 注意,传入的属性为 cc.Vec3,所以需要使用 Vec3.lerp 进行插值计算
    14. return start.lerp(end, t, current);
    15. }
    16. }
    17. })

    复制缓动

    clone 函数会克隆一个当前的缓动,并接受一个 target 作为参数。

    1. // 先创建一个缓动作为模板
    2. let tween = cc.tween().to(4, { scale: 2 })
    3. // 复制 tween,并使用节点 Canvas/cocos 作为 target
    4. tween.clone(cc.find('Canvas/cocos')).start()
    5. // 复制 tween,并使用节点 Canvas/cocos2 作为 target
    6. tween.clone(cc.find('Canvas/cocos2')).start()

    插入其他的缓动到队列中

    你可以事先创建一些固定的缓动,然后通过组合这些缓动形成新的缓动来减少代码的编写。

    1. let scale = cc.tween().to(1, { scale: 2 })
    2. let rotate = cc.tween().to(1, { rotation: 90})
    3. let move = cc.tween().to(1, { position: cc.v3(100, 100, 100)})
    4. // 先缩放再旋转
    5. cc.tween(this.node).then(scale).then(rotate)
    6. // 先缩放再移动
    7. cc.tween(this.node).then(scale).then(move)

    并行执行缓动

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

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

    回调

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

    重复执行

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

    1. cc.tween(this.node)
    2. .by(1, { scale: 1 })
    3. // 对前一个 by 重复执行 10次
    4. .repeat(10)
    5. // 最后 node.scale === 11
    6. .start()
    7. // 也可以这样用
    8. cc.tween(this.node)
    9. .repeat(10,
    10. cc.tween().by(1, { scale: 1 })
    11. )
    12. .start()
    13. // 一直重复执行下去
    14. cc.tween(this.node)
    15. .by(1, { scale: 1 })
    16. .repeatForever()
    17. .start()

    延迟执行

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