本文详细介绍如何在Three.js项目中使用Tween.js库创建平滑的补间动画。内容涵盖Tween.js的安装、核心方法如.to()、.start()、.chain()的使用,以及缓动函数的应用。通过Vue环境下的具体代码示例,演示如何实现物体的移动、缩放及多段动画串联,帮助开发者掌握前端3D动画的实现技巧。
tween.js介绍
Tween.js是一个用于生成平滑动画效果的JavaScript库,其官方仓库地址为:https://github.com/tweenjs/tween.js/。开发者可以将源码下载后,在tween.js/dist/文件夹下找到对应的js文件并在HTML中引用;或者通过npm命令在终端中安装tween.js模块。
npm install @tweenjs/tween.js然后在相应的页面引用Tween.js
import * as TWEEN from '@tweenjs/tween.js'
tween.js的使用方法
使用tween.js非常简便,仅需三个步骤即可完成一个补间动画的创建。
1、在创建Tween实例时,将需要修改的变量作为参数传递给Tween构造函数。
2、使用TWEEN.Tween().to()方法,传入目标状态的最终值以及动画持续时间这两个参数。
3、调用Tween().start()方法启动动画,此时tween引擎会计算从起始点到结束点之间的数值变化,从而生成平滑的动画效果。
tween.js的核心方法
.to()方法
控制补间的运动形式及方向。当tween启动时,Tween.js会读取当前属性值并应用相对值来计算新的最终值。
.start(time) 方法
这是启动补间动画的方法。.start方法接受一个可选参数time,如果提供了该参数,补间动画不会立即开始,而是直到指定的特定时刻才会启动。
.stop()方法
用于关闭正在执行的补间动画。调用.stop()可以立即终止该动画。
.repeat()方法
使用该方法可以使动画重复执行。它接受一个参数,用于指定动画需要重复的次数。
.delay()方法
用于延迟执行动画。.delay()方法接受一个参数,用于控制延迟的具体时间,即指定延迟多少时间后才开始执行动画。
.pause()方法
用于暂停当前的补间运动。该方法通常与.resume()方法配合使用,以实现动画的暂停与恢复控制。
.resume()方法
用于恢复之前被暂停的补间运动。调用该方法后,动画将从暂停点继续执行。
.yoyo() 方法
用于控制补间重复的模式。该功能仅在使用.repeat()时有效,它使动画像悠悠球一样来回运动,而不是在每次重复时重新开始。
.update()方法
用于动态更新补间动画。TWEEN.update()通常配合window.requestAnimationFrame使用,以确保持续刷新动画状态。
.chain()方法
用于实现链式补间动画。当我们需要顺序排列不同的补间动画,例如在前一个补间结束时立即启动另一个补间动画时,可以使用.chain()方法。
//tweenB动画在tweenA动画完成后执行
tweenA.chain(tweenB);
在一些情况下,可能需要将多个补间链接到另一个补间,以使它们(链接的补间)同时开始动画:
tweenA.chain(tweenB,tweenC);
注意:调用 tweenA.chain(tweenB) 实际上修改了tweenA,所以tweenA总是在tweenA完成时启动。 chain 的返回值只是tweenA,不是一个新的tween。
.getAll()方法
用于获取所有的补间组。调用TWEEN.getAll()可以返回当前管理的所有补间实例。
.removeAll()方法
用于删除所有的补间组。调用TWEEN.removeAll()可以清除所有已添加的补间动画。
.add()方法
用于新增补间。调用TWEEN.add(tween)可以将一个特定的补间 var tween=new TWEEN.Tween() 添加到全局管理器中。
.remove()方法
用于删除补间。调用TWEEN.remove(tween)可以删除一个特定的补间 var tween=new TWEEN.Tween()。
.Group()方法
用于新增一个补间组,
var Group=TWEEN.Group() , new TWEEN.Tween({ x: 1 }, Group) , 将已经配置好的补间动画进行分组 , TWEEN.update()和TWEEN.removeAll() , 不会影响到已经分好组的补间动画
tween.js回调函数
.onStart()补间动画开始时执行,只执行一次
new TWEEN.Tween().onStart((obj)=>{}) , 补间开始时执行,只执行一次, 当使用 repeat() 重复补间时,不会重复运行 ,
onStart((obj)=>{}) obj 补间对象作为第一个参数传入
.onStop() 停止补间动画时执行
new TWEEN.Tween().onStop((obj)=>{}) , 当通过 onStop() 显式停止补间时执行,但在正常完成时并且在停止任何可能的链补间之前执行补间,onStop((obj)=>{}) obj 补间对象作为第一个参数传入
.onUpdate() 每次更新时执行
new TWEEN.Tween().onUpdate((obj)=>{}) , 每次补间更新时执行,返回实际更新后的值, onUpdate((obj)=>{}) obj 补间对象作为第一个参数传入
.onComplete() 补间动画完成时执行
new TWEEN.Tween().onComplete((obj)=>{}) , 当补间正常完成(即不停止)时执行 , onComplete((obj)=>{}) obj 补间对象作为第一个参数传入
.onRepeat() 重复补间动画时执行
new TWEEN.Tween().onRepeat((obj)=>{}) , 当补间动画完成,即将进行重复动画的时候执行 , onComplete((obj)=>{}) `obj 补间对象作为第一个参数传入
TWEEN.Easing 缓动函数
tween.js为我们封装好了常用的缓动动画,如线性,二次,三次,四次,五次,正弦,指数,圆形,弹性,下落和弹跳等缓动函数 以及对应的缓动类型:In (先慢后快) ;Out (先快后慢) 和 InOut (前半段加速,后半段减速)
常见的缓动动画如下
- Linear:线性匀速运动效果;
- Quadratic:二次方的缓动(t^2);
- Cubic:三次方的缓动(t^3);
- Quartic:四次方的缓动(t^4);
- Quintic:五次方的缓动(t^5);
- Sinusoidal:正弦曲线的缓动(sin(t));
- Exponential:指数曲线的缓动(2^t);
- Circular:圆形曲线的缓动(sqrt(1-t^2));
- Elastic:指数衰减的正弦曲线缓动;
- Back:超过范围的三次方缓动((s+1)t^3 – st^2);
- Bounce:指数衰减的反弹缓动。
- 以上每个效果都分三个缓动类型,分别是:
- easeIn:从0开始加速的缓动,也就是先慢后快;
- easeOut:减速到0的缓动,也就是先快后慢;
- easeInOut:前半段从0开始加速,后半段减速到0的缓动。
在Threejs中使用Tween.js库
继续在前面章节的代码基础上进行实现,由于我们是基于vue开发的,所以这里我们使用npm的方式安装tween.js库
在vue中安装并引入tween.js库
打开控制器,输入npm install @tweenjs/tween.js进行安装,在components文件夹新建TweenView.vue文件,在该文件中引入tween.js
import * as TWEEN from '@tweenjs/tween.js'
初始化场景
在使用tween.js实现动画之前,先将threejs的初始化环境搭建好,并创建一个立方体
<template>
<div id="scene"></div>
</template>
<script setup>
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import * as TWEEN from '@tweenjs/tween.js'
import { onMounted } from 'vue'
let scene,camera,renderer,controls
onMounted(()=>{
init()
})
function init() {
initScene()
initCamera()
initAxesHelper()
initLight()
initRenderer()
initControls()
initMesh()
animate()
window.addEventListener('resize',onWindowResize.bind(this))
}
// 初始化场景
function initScene() {
scene = new THREE.Scene()
}
// 初始化相机
function initCamera() {
camera = new THREE.PerspectiveCamera(75,window.innerWidth / window.innerHeight,0.1,1000)
camera.position.set(0,2,2)
}
// 辅助轴
function initAxesHelper() {
const axesHelper = new THREE.AxesHelper(1)
scene.add(axesHelper)
}
// 灯光
function initLight() {
const hesLight = new THREE.HemisphereLight(0xffffff,0x444444)
hesLight.intensity = 0.3
scene.add(hesLight)
const dirLight = new THREE.DirectionalLight()
dirLight.position.set(5,5,5)
scene.add(dirLight)
const pointLight = new THREE.PointLight(0xffffff,1.5)
pointLight.position.set(0,100,90)
scene.add(pointLight)
pointLight.color.setHSL(Math.random(),1,0.5)
}
// 初始化渲染器
function initRenderer() {
renderer = new THREE.WebGLRenderer({antialias:true})
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(window.innerWidth,window.innerHeight)
document.querySelector('#scene').appendChild(renderer.domElement)
renderer.shadowMap.enable = true
}
// 初始化轨道控制器
function initControls() {
controls = new OrbitControls(camera,renderer.domElement)
controls.minPolarAngle = 0
controls.maxPolarAngle = 80 / 360 * 2 * Math.PI
controls.update()
}
// Mesh
function initMesh() {
const boxGeometry = new THREE.BoxGeometry(0.3,0.3,0.3)
const boxMaterial = new THREE.MeshPhongMaterial({color:0x00ff00})
const boxMesh = new THREE.Mesh(boxGeometry,boxMaterial)
scene.add(boxMesh)
}
function animate() {
const delta = clock.getDelta()
renderer.render(scene,camera)
controls.update(delta)
requestAnimationFrame(animate)
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth,window.innerHeight)
}
</script>
<style scoped>
</style>
实例化Tween对象
实例化Tween对象并将物体当前的位置作为参数传入该对象
const tween = new TWEEN.Tween(mesh.position)
使用.to()方法指定移动终点和时间
使用.to()方法指定移动的终点和时间
tween.to({x:3,y:0,z:0},2000)
调用.onUpdate()方法更新动画,
调用.onUpdate()方法更新动画,在回调函数中设置相机的lookAt()方法,
tween.onUpdate(function(){
camera.lookAt(0,0,0)
})
开启动画
使用.start()方法开启动画
tween.start()
刷新查看浏览器, 发现物体已经按照我们给定的终点坐标进行了移动

给场景添加地面辅助线
为了更好的看到效果,给场景中添加地面辅助线,定义initGridHelper()方法,在init()函数中调用
// 辅助地面
function initGridHelper () {
const gridHelper = new THREE.GridHelper(10,10)
scene.add(gridHelper)
}
使用.chain()方法对多段动画进行串联执行
继续对上面的物体运动进行研究,我们在物体移动到指定位置后,再给定一个目标点位置,使其继续移动;
新建一个tween2对象,仍然将mesh.position作为参数传入,调用tween2.to方法,指定终点坐标和时间
然后通过调用.chain()方法,将tween2作为参数传递给tween.chain()方法,这样tween在执行完第一段代码后,会接着执行tween2的代码,实现多段动画的连续执行
const tween2 = new TWEEN.Tween(mesh.position)
tween2.to({x:3,y:0,z:3},2000)
tween.chain(tween2)
tween.start()
使用tween执行缩放动画
tweenjs不仅仅能实现移动动画,它能实现很多动画功能,这里我们接着上面的动画在物体移动到tween2指定的终点时,再对齐进行一个缩放动画 我们先定义一个对象,里面给一个参数s为1,代表当前的缩放比例是1
const scaleParam = { s:1}
我们创建一个新的tween3对象然后我们创建一个新的tween3对象,将上面的scaleParam 作为参数传入
const tween3 = new TWEEN.Tween(scaleParam)
调用.to方法调用tween3.to()方法,将对象s设置为10,时间设置为2000
tween3.to({s:10},2000)
调用.onUpdate()方法调用.onUpdate()方法,在.onUpdate()方法的回调函数中设置mesh在x轴的缩放值为s
tween3.onUpdate(function(){
mesh.scale.x = scaleParam.s
})
调用.chain()方法调用tween2.chain()方法,将tween3作为参数传入,表示tween2动画执行完后执行tween3动画
tween2.chain(tween3)
tween.start()
刷新浏览器,可以看到物体按照我们预期的效果实现了动画

使用缓动动画
使用缓动动画可以是我们的动画看起来更真实自然,前面讲过,tweenjs已经为我们封装了常见的缓动动画,我们使用使只需要调用就可以了,常见的缓动动画曲线如下图所示

给立方体运动的动画添加缓动动画我们可以给上面的立方体添加缓动动画,使其看起来更真实,我们在立方体的第一段动画(tween)和第二段动画(tween2)时让其先快后慢
const tween = new TWEEN.Tween(mesh.position)
tween.to({x:3,y:0,z:0},2000)
tween.onUpdate(function(){
camera.lookAt(0,0,0)
}).easing(TWEEN.Easing.Sinusoidal.InOut)
const tween2 = new TWEEN.Tween(mesh.position)
tween2.to({x:3,y:0,z:3},2000).easing(TWEEN.Easing.Sinusoidal.InOut)
tween.chain(tween2)
const scaleParam = { s:1}
const tween3 = new TWEEN.Tween(scaleParam)
tween3.to({s:10},2000)
tween3.onUpdate(function(){
mesh.scale.x = scaleParam.s
})
tween2.chain(tween3)
tween.start()
刷新浏览器看效果,符合我们的要求

好了,关于tweenjs和threejs结合创建动画就先说到这里,其实关于tween和threejs结合的动画还有很多,比如可以结合tween实现物体颜色的变化、透明度的变化等,具体各位小伙伴可以自己摸索。
总结
到此这篇关于Threejs与Tween.js结合创建动画的文章就介绍到这了,更多相关Threejs与Tween.js创建动画内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!