系统学习一下Tween.js的使用,对Tween.js官方使用文档进行了翻译,有很多操作细节还有使用方法,收获很多啊!
学习链接
- Tweenjs官方文档
- tweenjs.github.io/tween.js/do…
简介
Tween的概念:
- 以一种平滑的方式改变对象的值,
- 我们只需要设置需要改变的参数,目标值,运行时间,Tween引擎就会自动帮我们搞定它了。
举一个例子!我们有一个对象position,里面有x 和 y属性
var position = {x: 100, y: 0}
// Create a tween for position first
// 创建一个tween对象,初始值为position,
var tween = new TWEEN.Tween(position)
// Then tell the tween we want to animate the x property over 1000 milliseconds
// 设置position里面的x ,设置时间为1s
tween.to({x: 200}, 1000)
这样就设置好了,但是还不起作用,需要手动启动它,调用start函数
// And set it to start
// 调用tween对象的 start方法
tween.start()
最后一步,启动start方法后,还需要在循环中调用update函数
animate()
function animate() {
requestAnimationFrame(animate)
TWEEN.update() //<----------
}
这样,我们就设置完全部的内容了,它会在一秒的时间内,从x 为100 移动到200
如果想要查看 x 值的改变情况,可以在onUpdate回调函数中打印出来
- onUpdate方法会在 tween 执行的周期内运行!
tween.onUpdate(function (object) {
console.log(object.x)
})
在Threejs中使用
- 移动cube的位置,假如初始位置为 {x: 0, y: 0, z: 0},移动到{x: 100, y: 100, z: 100}
- 使用时间 10s
var tween = new TWEEN.Tween(cube.position).to({x: 100, y: 100, z: 100}, 10000).start()
animate()
function animate() {
requestAnimationFrame(animate)
TWEEN.update()
threeRenderer.render(scene, camera)
}
这里用了链式调用!
// 01
var tween = new TWEEN.Tween(position).to({x: 200}, 1000).start()
// 02
var tween = new TWEEN.Tween(position)
tween.to({x: 200}, 1000)
tween.start()
tween.js 执行动画
Tween.js自身并不能自动运行,需要在周期函数中运行,推荐的函数就是requestAnimationFrame,在周期函数中调用TWEEN.update()
animate()
function animate() {
requestAnimationFrame(animate)
// [...]
TWEEN.update() //<----------
// [...]
}
控制 tween 对象
start和stop
start方法,tween执行动画的起点,与之相对应的是stop方法,停止动画
tween.stop();
注!停止一个没有启动的 tween 和已经停止的 tween 是没有效果的,但是也不报错!
update
每一个tween对象,都有update方法,用在周期函数中
- 我们创建的tween,如果没有分组,都是在默认的组里的,直接使用TWEEN.update()做更新即可
- 如果使用Tween.Group()进行分组,需要对该组进行update操作!,(下文介绍分组)
chain
链接:可以在一个tweeen对象执行结束后,继续执行另一个tween对象!
// tweenA执行完,执行tweenB
tweenA.chain(tweenB)
// 无限循环
tweenA.chain(tweenB)
tweenB.chain(tweenA)
// 链接多个tween对象!执行多个动画!
tweenA.chain(tweenB, tweenC)
注!WARNING: Calling
tweenA.chain(tweenB)actually modifies tweenA so that tweenB is always started when tweenA finishes. The return value ofchainis just tweenA, not a new tween.
上文的意思是,我们通过chain的方法链接tweenB,实际上是修改tweenA的内容,这样tweenB就会在tweenA执行完后执行,如果返回当前chain的值,得到的还是tweenA
repeat
重复:如果相要一个tween动画,一直执行下去,可以chain自身,不过更好的方法是,使用repeat方法,通过repeat方法,可以设置重复的次数
- 比如说,从(0,0,0)位置,移动到(1,1,1)位置
- 每次都从(0,0,0)位置出发,重复执行
// 重复10次
tween.repeat(10) // repeats 10 times after the first tween and stops
// 重复无限次
tween.repeat(Infinity) // repeats forever
yoyo
这个方法,只在使用了repeat方法后,才能起作用,可以从移动的终点,返回到起点,就像悠悠球一样!
delay
延迟:更复杂的动画,可能就需要延迟设置了,可以在启用start方法后,执行动画
// 1s后执行动画,
tween.delay(1000)
tween.start()
repeatDelay
可以让延迟操作,重复执行!
- 第一动画执行时间,在1s后
- 第二个动画执行时间,在第一个执行完,0.5s后
- 第三个动画执行时间,在第二个执行完,0.5s后
tween.delay(1000) // 首次
tween.repeatDelay(500) // 首次之后的。
tween.start()
dynamic
动态:默认为false,主要结合的是 tween.to()方法。
- to设置的是终点的位置,默认是false,就是动画开始,起点和终点都确定好了。
- 设置为true,就可以动态的修改终点的位置了!
- Tween.js / dynamic to… - 案例地址
控制 tween 对象 - All
这里介绍的是,tween对象的全局方法,并不是都需要使用,最常用的还是update方法。
TWEEN.update(time)
- 用于更新tween对象(默认分组内的!)
TWEEN.getAll & TWEEN.removeAll
Used to get a reference to the active
tweensarray and to remove all of them from the array with just one call, respectively.
TWEEN.getAll用于获取一组 tween对象TWEEN.removeAll用于移除一组 tween对象
TWEEN.add(tween) & TWEEN.remove(tween)
Used to add a tween to the list of active tweens, or to remove a specific one from the list, respectively.
These methods are usually used internally only, but are exposed just in case you want to do something funny.
TWEEN.add用于添加 tween对象,到分组中TWEEN.remove用于从分组中移除 tween对象
以上方法,多用于tween.js内部。
tween 分组
我们创建的tween对象,都在默认的分组中,更新动画只需要在周期函数中使用 TWEEN.update()
为什么需要分组?
- 如果项目很大,有很多的
tween对象,它们都处于默认的分组中 - 执行
TWEEN.update()orTWEEN.removeAll(),会把所有的都更新 或移除了 - 进行分组的话,就可以有效的控制哪些需要更新,哪些需要移除了!
设置缓动动画!
把时间分段,在不同的分段中,移动不同的速度!
// 描述一下运行流程。。
// 开始的时候慢,中间慢慢加速,到终点最快。。
// 可以看下面的图表。
tween.easing(TWEEN.Easing.Quadratic.In)
案例图表
如何自定义缓动动画?
- 略,没有看懂。。
- 可以查看案例代码。
案例图表
tween对象回调函数
可以在tween对象的生命周期内调用!如果对象的修改,需要通过set函数,就可以使用回调函数了!
举个例子。
- 使用
onUpdate() - 在回调函数中,用set方法。
- 使用
onStart() - 动画开始时,播放音乐
onStart
运行时间
- 在启动
start()方法之前 // 这样所有的开始都同步了! - 在
delay()方法之后!// 当然应该在延迟之后执行了。。 - 如果tween对象,启用repeat()方法,不起作用
- 适用场景 - 同步所有需要一起执行的动画!
onStop
运行时间
- 通过stop()显示停止动画时执行!, 一定要启动stop方法!
- 不会在动画结束执行
- 不会在链式调用下一个tween对象时执行!
onUpdate
运行时间
- 只要动画执行就会执行!
onComplete
运行时间
- 动画正常结束的时候执行,(没有调用stop方法。。)
onRepeat
运行时间
- 动画完成,即将进行重复动画的时候执行!!
tween对象进阶
相对参数!
- 主要用在to方法中的,看这里的"+"号
- 计算位置不同!
嵌套对象!
- Tween.js可以修改嵌套对象内的值!
var nestedObject = {scale: {x: 0, y: 0}, alpha: 0}
var tween = new TWEEN.Tween(nestedObject).to({scale: {x: 100, y: 100}, alpha: 1})
to方法中,使用数组
- 使用数组,可以在动画执行周期内,控制移动位置。
// 运行开始,在0的位置,时间走到一半,在-100,终点在100
var tween = new TWEEN.Tween(relativeObj).to({x: [0, -100, 100]})
数组支持三种动画效果
- Linear - 线性 (默认)
- Bezier - 贝塞尔
- CatmullRom - 插值
- Tween.js / array inte… - 效果查看
最佳使用规范
css动画
不要通过top 和left 来改变移动位置,它会导致重绘(改变样式)和回流(改变布局),很消耗性能。
// 通过 top 和left 来改变位置 - 不要这样!
var element = document.getElementById('myElement')
var tween = new TWEEN.Tween({top: 0, left: 0}).to({top: 100, left: 100}, 1000).onUpdate(function (object) {
element.style.top = object.top + 'px'
element.style.left = object.left + 'px'
})
应该使用transform属性来改变位置!不会回流,硬件加速也可能支持。
- 补充阅读资料
- Why moving elements with translate() is better than pos:abs top/left - Paul Irish - 作者来自Google Chrome team
var element = document.getElementById('myElement')
var tween = new TWEEN.Tween({top: 0, left: 0}).to({top: 100, left: 100}, 1000).onUpdate(function (object) {
element.style.transform = 'translate(' + object.left + 'px, ' + object.top + 'px)'
})
如果我们的动画效果比较简单,通过css 的
animation和transition就可以了,Tween.js主要用于复杂的动画效果。
垃圾回收
在使用onUpdate回调函数时,需要额外注意,它会在动画执行周期内,一直运行,如果在其中放入过多的操作,会占用大量的性能,需要保证onUpdate回调函数中的内容尽量轻便,不要有太多的操作。
crazy tweening
- 这里好有意思,不常用
- Tween.js可以把输入的数据,以不同的速率执行!
- 文中举了个声音的例子
- tween.audio (5013.es) - 使用tween.js修改声音的播放速率!!
推荐案例
官方案例代码
Threejs!
总结
翻译文档,是一个很棒的学习方法啊!
整理一下内容!
-
我们在一开始,了解了Tween.js的运行流程,需要
new,to(),start(),TWEEN.update() -
然后学习了Tween.js的方法【
start(),stop(),update()等】,和回调函数【可以控制的细节更多!,onStart(),onStop(),repeat(),yoyo()等】 -
还有分组,设置缓动动画,最后给了一些实用小建议。
别让乌云遮住了天空的蓝,别让命运折返没桨的船,别让黑夜都落在你的臂弯,让我来与你作伴