Tween.js
是一个 过渡计算工具包 ,理论上来说只是一个工具包,可以用到各种需要有过渡效果的场景,实际上也是这样。 不个人目前最常用的就是与 threejs
结合使用。
一、引入工具包
npm install @tweenjs/tween.js
引入
var TWEEN = require('@tweenjs/tween.js');
个人习惯性的用 npm 安装引入 ,如果不习惯,下载源文件并通过 script
标签套进来也是一样的。
一般情况下,需要通过环境里面提供的辅助时间工具来实现。比如在网页上就是使用 window
下的 requestAnimationFrame
方法,低端浏览器只能使用 setInterval
方法来配合了。
二、简单使用
例如:把一个元素的 x 坐标在 1秒内由100 增加到 200, 通过以下代码可以实现:
// 1. 定义元素
var position = { x: 100, y: 0 };
// 2. new 一个 Tween对象,构造参数传入需要变化的元素
var tween = new TWEEN.Tween(position);
// 3. 设置目标
tween.to({ x: 200 }, 1000);
// 4. 启动,通常情况下,2,3,4步一般习惯性写在一起,定义命名也省了
// 例如 new TWEEN.Tween(position).to({ x: 200 }, 1000).start();
tween.start();
// 5. (可选:)如果在变化过程中想自定义事件,则可以通过 `onUpdate` 实现
tween.onUpdate(function(pos) {
console.log(pos.x);
});
// 6. 设置 requestAnimationFrame,在方法里面调用全局的 `TWEEN.update()`
// 在这个方法里面也可以加入其它的代码:d3, threejs 里面经常会用到。
// 比如THREEJS里面用到的变化,如 `renderer.render(scene, camera);` 等
function animate() {
requestAnimationFrame(animate);
TWEEN.update();
}
// 6. 启动全局的 animate
animate();
三、设置变化参数
上面代码中,参数主要是通过 new 操作
和 to 方法
设置进入的。大部分时候可以满足需要,
to
方法设置元素过渡的时间和目标值即可。
连续型变化
一般情况下,设置起点和终点时两个数时,认为是连续型的,里面有无数的可能变化到的值(算上小数)。
连续型变化使用 easing
做为时间变化函数,默认使用的是 Liner
纯性过渡函数,easing过渡
的种类很多,每种还区分 IN, OUT, INOUT 算法。引用时 使用 TWEEN.Easing
前缀,例如 :
tween.easing(TWEEN.Easing.Quadratic.Out)
目前内置的过渡函数下图:
离散型变化
** 离散型变化 ** :也就是非连续型的,比如变化过程只能是在 [1,2,3,4,5]
5个值中进行(跳跃,不会1 -> 1.5 -> 2这种 )变化,那么应该:
// to 方法中设置的 target 将对象的值以数组的形式传入:
var tween = new TWEEN.Tween(relativeObj).to({ x: [ 1, 2, 3, 4, 5]});
// 设置后会根据值的数量(5个值时) ,分别时间经过 0%, 25%, 50%, 75%, 100% 时返回。
数量过渡默认使用的是 TWEEN.Interpolation.Linear
, 也可以使用 interpolation
方法做为时间变换的参数,除了 TWEEN.Interpolation.Linear
之外,还支持 TWEEN.Interpolation.Bezier
, TWEEN.Interpolation.CatmullRom
自定义过渡函数
不管是连续型还是离散型,都是支持自定义函数的,如果对于 Tween 内置的不满意或不满足条件,可以自定义一个函数按自己的算法实现过渡,例如:
function stepEasing(k) {
return Math.floor(k * 10) / 10;
}
tween.easing(stepEasing);
四、主动控制
Demo 里面,我们使用了 start()
方法启动了过渡,x 会从 start 开始从初始值,经过 to()
方法设置的时间后,再到 to()
方法设置的值。在变化的过程中,使用 update()
方法将事件发出。
start([time])
, stop()
开始,结束,start
方法还接受一个时间参数,如果传了的话,就直接从传入的时间开始
update([time])
更新,会触发更新事件,如果有监听,则可以执行监听相关代码。
更新方法接受一个时间参数,如果传了的话,就更新到指定的时间
chain(tweenObject,[tweenObject])
如果是多个 Tween 对像
如果 tweenA
需要等到 tweenB
结束后,才能 start
, 那么可以使用 。
tweenA.chain(tweenB);
//另外,chain方法也可以接收多个参数,如果 A对象 需要等待多个对像时,依次传入
tweenA.chain(tweenB, tweenC, tweenD);
当然,你还可以利用这个特性,创建一个两个对像的无限循环
tweenA.chain(tweenB);
tweenB.chain(tweenA);
repeat(number)
循环的次数,默认是 1
所以不定义的话,只过渡一次就没了,可以使用上面刚说的无限循环的方法,便只是一个对象无限循环时,就使用 :
tween.repeat(Infinity); //无限循环
tween.repeat(5); //循环5次
delay(time)
delay
是指延时多久才开始。比如以下代码
tween.delay(1000);
tween.start();
虽然已经调用 start
了,但过渡动作要等 1秒 后才开始执行!
监听 onStart
, onStop
, onUpdate
, onComplete
分别对应 生命周期里面的 start
, stop
, update
, complete
, 如果 设置了这些监听事件,那么执行到对应部分时,会进行调用。
每个方法都接收一个 类型为 function(object){}
回调函数做为参数,当事件触发时,会将监听对象的实体传入到回实体。
五、全局控制
全局控制函数
全局控制函数平时使用的并不是太多, 简单带过一下:
TWEEN.update(time)
:对所有active
状态的Tween
对象实行update(time)
调用TWEEN.getAll()
,TWEEN.removeAll()
: 获取 删除所有的Tween对象
TWEEN.add(tween)
,TWEEN.remove(tween)
: 自定义添加,删除Tween对象
,这个方法在Tween对象
的生命周期里面自动调用的,不需要手动调用,提供 API 可能是让各位能发挥想象空间做点更炫酷的功能!
分组控制
对于复杂的动画来说,可能需要同时管理成百上千个 Tween对象
,TWEEN
提供了分组控制的,以便于复杂的组合。
var groupA = new TWEEN.Group();
var groupB = new TWEEN.Group();
var tweenA = new TWEEN.Tween({ x: 1 }, groupA)
.to({ x: 10 }, 100)
.start();
var tweenB = new TWEEN.Tween({ x: 1 }, groupB)
.to({ x: 10 }, 100)
.start();
var tweenC = new TWEEN.Tween({ x: 1 })
.to({ x: 10 }, 100)
.start();
groupA.update(); // only updates tweenA
groupB.update(); // only updates tweenB
TWEEN.update(); // only updates tweenC
groupA.removeAll(); // only removes tweenA
groupB.removeAll(); // only removes tweenB
TWEEN.removeAll(); // only removes tweenC
六、小结
其实我是不太想用这个工具包的,但使用 threejs
要用的很多动画,官方没有提供动画工具,而不得已才使用。
个人更一喜欢 d3
的动画方式, 使用selector.transition()
来完成一个动画,与环境结合的更紧密,后续的各种动画都会以 selector
做为上下文在应用中,希望 threejs
早点出一个像 d3
一样的能包含上下文的动画 API :jack_o_lantern: