补间动画
补间动画指的是做FLASH动画时,在两个关键帧中间需要做“补间动画”,才能实现图画的运动;插入补间动画后两个关键帧之间的插补帧是由计算机自动运算而得到的。
——摘自《百度百科:补间动画_百度百科》
Tween.js
Tween.js的GitHub项目地址:GitHub - tweenjs/tween.js: JavaScript/TypeScript animation engine,
官网文档地址:tween.js 用户指南 | tween.js。
利用ThreeJS自带的Tween.js可以很方便的实现补间动画,直接导入即可使用,
import { Tween } from 'three/examples/jsm/libs/tween.module.js'; //Tween.js
Tween基本使用
//TODO:添加1个球体
const ball1 = new THREE.SphereGeometry(1);
const ballMaterial = new THREE.MeshBasicMaterial({color:0xffff00});
const ballMesh1 = new THREE.Mesh(ball1,ballMaterial);
ballMesh1.geometry.scale(0.5,0.5,0.5);
ballMesh1.position.set(0,0,0);
scene.add(ballMesh1);
假设现在创建了一个小球,我们要使用Tween为其设置动画效果,
//TODO:Tween.js补间动画
const tween = new TWEEN.Tween(ballMesh1.position);
tween.to({x:4},1000)
.onUpdate(function(vector3Position){
//监听位置变化
console.log(vector3Position);
});
//设置重复次数-[2次]
tween.repeat(2);
//设置重复次数-[无数次]
// tween.repeat(Infinity);
//设置循环往复
tween.yoyo(true);
//设置延迟执行时间
tween.delay(1000);
//在animate循环渲染函数中调用updateTween
function updateTween(){
tween.update();
}
以上代码,就可实现ballMesh小球沿着X轴,循环往复运动2次的动画效果。
Tween缓动函数
缓动函数:用于表示自定义参数随时间变化的速率。
Tween.js提供的缓动函数可参考:Tween.js / graphs,
通过使用缓动函数,可以模拟物体的匀变速运动(加速/减速)运动。
例如,使用缓动函数Quadratic.In,来模拟物体的匀加速运动,修改上述代码如下,
//TODO:Tween.js补间动画
const tween = new TWEEN.Tween(ballMesh1.position);
tween.to({x:4},1000)
.onUpdate(function(vector3Position){
//监听位置变化
console.log(vector3Position);
});
//设置重复次数-[2次]
// tween.repeat(2);
//设置重复次数-[无数次]
tween.repeat(Infinity);
//设置循环往复
tween.yoyo(true);
//设置延迟执行时间-【可以避免动画的跳动现象】
tween.delay(100);
//设置缓动函数
tween.easing(TWEEN.Easing.Quadratic.In);
function updateTween(){
TWEEN.update();
}
Tween链接补间
当你按顺序排列不同的补间时,事情会变得更有趣,例如在上一个补间结束的时候立即启动另外一个补间。我们称此为链接补间,它是通过 chain
方法完成的。因此,要使 tweenB
在 tweenA
完成后开始:
tweenA.chain(tweenB)
或者,可以创造一个无限的链式,tweenA
完成时开始 tweenB
,tweenB
完成时开始 tweenA
:
tweenA.chain(tweenB)
tweenB.chain(tweenA)
在其他情况下,你可能希望将多个补间链接到另一个补间,使它们(链接的补间)都同时开始动画:
tweenA.chain(tweenB, tweenC)
Warning 调用
tweenA.chain(tweenB)
实际上修改了 tweenA,所以 tweenB 总是在 tweenA 完成时启动。chain
的返回值只是 tweenA,不是一个新的 tween。
例如:我们使用chain链接补间的方式,实现上面的循环往复运动,
//TODO:Tween.js补间动画
const tween = new TWEEN.Tween(ballMesh1.position);
tween.to({x:4},1000)
.onUpdate(function(vector3Position){
//监听位置变化
console.log(vector3Position);
});
//设置缓动函数
tween.easing(TWEEN.Easing.Quadratic.In);
const tweenBack = new TWEEN.Tween(ballMesh1.position);
tweenBack.to({x:0,},1000);
//设置缓动函数
tween.easing(TWEEN.Easing.Quadratic.Out);
//创建补间动画
tween.chain(tweenBack);
tweenBack.chain(tween);
//启动动画tween
tween.start();
function updateTween(){
TWEEN.update();
}
通过以上代码,也可以实现yoyo()的效果。
Tween动画生命周期回调函数
利用Tween.js构建的动画包含:开始、更新、停止、完成几个生命周期阶段,Tween.js也提供了对应的生命周期回调函数,如下图所示,
Tween.js最佳性能实践
虽然 Tween.js 试图靠自己发挥性能,但没有什么能阻止你以反性能的方式使用它。 以下是一些在使用 Tween.js 时(或通常在 Web 中制作动画时)可以避免拖慢项目速度的方法。
使用高性能的 CSS
当你尝试为页面中元素的位置设置动画时,最简单的解决方案是为 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
,它不会使布局无效,并且在可能的情况下也会进行硬件加速,如下所示:
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,看看这篇文章。
但是,如果你的动画需求就这么简单,最好只使用 CSS 动画或过渡(在适用的情况下),这样浏览器就可以尽可能地进行优化。 当你的动画需要涉及复杂的操作时,Tween.js 是非常有用,也就是说,你需要将多个补间同步在一起,在一个完成后开始,循环多次,具有不是使用 CSS 而是使用 Canvas 渲染的图形或 WebGL 等等。
对垃圾收集器(别名 GC)
如果你使用 onUpdate
回调,你需要非常小心你放在它上面的东西。 这个函数每秒会被调用很多次,所以如果你在每次更新时都做代价高昂的操作,你可能会阻塞主线程并导致可怕的卡顿,或者——如果你的操作涉及内存分配,你最终会得到 垃圾收集器运行过于频繁,也会导致卡顿。 所以不要做这两件事。 保持你的 onUpdate
回调非常轻量级,并确保在开发时也使用内存分析器。
疯狂的补间
这是你可能不经常使用的东西,但你可以在 Tween.js 之外使用补间方程式。 毕竟,它们只是函数。 因此,你可以使用它们来计算平滑曲线作为输入数据。