Tween.js在Three.js中的应用:为3D动画添加流畅过渡

前言

Web开发领域,Three.js已经成为构建精彩3D内容的首选库之一。它让开发者能够轻松地在浏览器中创建和展示复杂的3D场景。然而,要让这些场景栩栩如生,平滑的动画效果是必不可少的。这就引入了Tween.js——一个轻量级但功能强大的JavaScript库,专门用于在Web应用中创建平滑的补间动画。本文将探讨如何在Three.js项目中集成并利用Tween.js来增强3D对象的动画表现。

简介

Tween.js 是一个简单易用的库,专注于数值的平滑插值(interpolation),非常适合于实现动画效果。无论是简单的颜色渐变、对象位置移动,还是复杂的序列动画,Tween.js都能轻松应对。在与Three.js结合时,它能显著提升3D场景的互动性和视觉吸引力。

安装Tween.js

官方地址为:https://github.com/tweenjs/tween.js

npm install tween.js

当然three.js包中自带的包含tween.js,其中three.js得tween地址为:node_moduls>three>examples>jsm>libs>tween.module.js

导入补间动画

import * as TWEEN from 'three/examples/jsm/libs/tween.module.js'

tween.js的核心方法

.to()方法

        控制补间的运动形式及方向.to(), 当tween启动时,Tween.js将读取当前属性值并 应用相对值来找出新的最终值。

.start(time) 方法

        补间动画启动的方法,.start方法接受一个参数 time , 如果加入这个参数,那么补间不会立即开始直到特定时刻才会开始

.stop()方法

        关闭补间动画 .stop() , 关闭这个正在执行的补间动画。

.repeat()方法

        使用该方法可以使动画重复执行,它接受一个参数 , 描述需要重复多少次。

.delay()方法

        延迟执行动画的方法.delay(), 接受一个参数用于控制延迟的具体时间,表示延迟多少时间后才开始执行动画。

.pause()方法

        暂停动画.pause() , 暂停当前补间运动,与resume方法配合使用。

.resume()方法

        恢复动画 .resume() , 恢复这个已经被暂停的补间运动。

.yoyo() 方法

        控制补间重复的模式 .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 (前半段加速,后半段减速)
Tween.js在Three.js中的应用:为3D动画添加流畅过渡

常见缓动动画

  1. Linear:线性匀速运动效果;
  2. Quadratic:二次方的缓动(t^2)
  3. Cubic:三次方的缓动(t^3)
  4. Quartic:四次方的缓动(t^4)
  5. Quintic:五次方的缓动(t^5)
  6. Sinusoidal:正弦曲线的缓动(sin(t))
  7. Exponential:指数曲线的缓动(2^t)
  8. Circular:圆形曲线的缓动(sqrt(1-t^2))
  9. Elastic:指数衰减的正弦曲线缓动;
  10. Back:超过范围的三次方缓动((s+1)t^3 – st^2)
  11. Bounce:指数衰减的反弹缓动。

以上每个效果都分三个缓动类型,分别是:
easeIn:从0开始加速的缓动,也就是先慢后快;
easeOut:减速到0的缓动,也就是先快后慢;
easeInOut:前半段从0开始加速,后半段减速到0的缓动。

Tween.JS和Three.js示例

Tween.js在Three.js中的应用:为3D动画添加流畅过渡

  1. 导入动画组件库
import * as TWEEN from "three/examples/jsm/libs/tween.module.js"
  1. 创建一个圆柱几何
const sphere1 =  new THREE.Mesh(
new THREE.CylinderGeometry(1, 1, 1, 64),
  new THREE.MeshBasicMaterial( {color: 0xffff00} )
)
sphere1.position.set(0, 10, 0)
sphere1.position.x = 0      
sphere1.position.y = 0      
sphere1.position.z = 0
sphere1.scale.set(-1, -1, 1) 
sphere1.rotation.z = -Math.PI/2 
this.scene.add(sphere1)
  1. 创建tween实例和tween动画
const tween = new TWEEN.Tween(sphere1.position)      
const tweenXZ = new TWEEN.Tween(sphere1.rotation)
const tween2 = new TWEEN.Tween(sphere1.position)
const tween3 = new TWEEN.Tween(sphere1.position)
const tween4 = new TWEEN.Tween(sphere1.position)
//移动
tween.to({x:4},300).onUpdate(()=>{})
//旋转
tweenXZ.to({y:-Math.PI/2},150).onUpdate(()=>{})
//移动
tween2.to({y:-4},300).onUpdate(()=>{})
//移动
tween3.to({x:0},300).onUpdate(()=>{})
//移动
tween4.to({y:0},300).onUpdate(()=>{})
// 一边移动一边旋转,动画在tween动画完成后执行tween2,并带有旋转效果
tween.chain(tween2,tweenXZ)
tween2.chain(tween3,tweenXZ)
tween3.chain(tween4,tweenXZ)      
tween4.chain(tween,tweenXZ)

  1. 设置动画速度运行曲线
tween.easing(TWEEN.Easing.Quadratic.Inout) 
  1. 开启动画
 tween.start()

  1. 开启动画后还不能完全动起来,还需要逐帧更新动画
 animate() {
      this.controls.update()
      TWEEN.update()
      requestAnimationFrame( this.animate );
      this.renderer.render( this.scene, this.camera );
    }

完整代码:

<template>
  <div id="container">
    
  </div>
</template>

<script>
import * as THREE from 'three'
// webGL兼容
import WebGL from 'three/examples/jsm/capabilities/WebGL.js';
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
// 轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"
//导入RGBRload加载器
import { RGBELoader } from  "three/examples/jsm/loaders/RGBELoader.js"
//导入场景模型加载器
import {GLTFLoader} from "three/examples/jsm/loaders/GLTFLoader.js"
//导入模型解压器
import {DRACOLoader} from "three/examples/jsm/loaders/DRACOLoader.js"
//导入Tween动画组件库
import * as TWEEN from "three/examples/jsm/libs/tween.module.js"
export default {
  name: 'Three9',
  components: {
  },
  mounted(){
    this.init()
  },
  data(){
    return {
      camera: null,  //相机对象
      scene: null,  //场景对象
      renderer: null,  //渲染器对象
      mesh: null,  //网格模型对象Mesh
      mesh2:null,
      controls:null, //轨道控制器
      material2:null, //父元素
      planeMesh:null, //平面
      rgbeLoacer:null,
    }
  },
  methods:{
    //随机生成十六进制颜色
    color16(){//十六进制颜色随机
			var r = Math.floor(Math.random()*256);
			var g = Math.floor(Math.random()*256);
			var b = Math.floor(Math.random()*256);
			var color = '#'+r.toString(16)+g.toString(16)+b.toString(16);
			return color;
    },
    init(){
      let container = document.body;
      //创建一个场景
      this.scene = new THREE.Scene()
      //透视摄像机
      this.camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,700)
      //创建渲染器
      this.renderer = new THREE.WebGLRenderer();
      //渲染器尺寸
      this.renderer.setSize( window.innerWidth,  window.innerHeight );    
      
      // 创建三个球
      const sphere1 =  new THREE.Mesh(
        new THREE.CylinderGeometry(1, 1, 1, 64),
        new THREE.MeshBasicMaterial( {color: 0xffff00} )
      )
      sphere1.position.set(0, 10, 0)
      sphere1.position.x = 0      
      sphere1.position.y = 0      
      sphere1.position.z = 0
      sphere1.scale.set(-1, -1, 1) 
      sphere1.rotation.z = -Math.PI/2 
      this.scene.add(sphere1)
      const tween = new TWEEN.Tween(sphere1.position)      
      const tweenXZ = new TWEEN.Tween(sphere1.rotation)
      const tween2 = new TWEEN.Tween(sphere1.position)
      const tween3 = new TWEEN.Tween(sphere1.position)
      const tween4 = new TWEEN.Tween(sphere1.position)
      //移动
      tween.to({x:4},300).onUpdate(()=>{})
      //旋转
      tweenXZ.to({y:-Math.PI/2},150).onUpdate(()=>{})
      //移动
      tween2.to({y:-4},300).onUpdate(()=>{})
      //移动
      tween3.to({x:0},300).onUpdate(()=>{})
      //移动
      tween4.to({y:0},300).onUpdate(()=>{})
      // 一边移动一边旋转,动画在tween动画完成后执行tween2,并带有旋转效果
      tween.chain(tween2,tweenXZ)
      tween2.chain(tween3,tweenXZ)
      tween3.chain(tween4,tweenXZ)      
      tween4.chain(tween,tweenXZ)
      //动画运行速度曲线
      tween.easing(TWEEN.Easing.Quadratic.Inout) 
      tween.start()
      this.scene.background=new THREE.Color(0x999999)
      // 设置相机位置
      this.camera.position.z = 15;   
      this.camera.position.y =2;  
      this.camera.position.x = 2; 
      // 看的方向 
      this.camera.lookAt(0,0,0)
      //添加世界坐标辅助器
      const axesHelper = new THREE.AxesHelper(3) 
      this.scene.add( axesHelper );
      //添加轨道控制器
      this.controls = new OrbitControls(this.camera,this.renderer.domElement)
      //添加阻尼带有惯性
      this.controls.enableDamping = true
      //设置阻尼系数
      this.controls.dampingFactor = 0.05
      //元素中插入canvas对象
      container.appendChild(this.renderer.domElement); 
      if ( WebGL.isWebGLAvailable() ) {
        this.animate();
      } else {
        const warning = WebGL.getWebGLErrorMessage();
        document.getElementById( document.body ).appendChild( warning );
      }
    },
    //旋转起来
    animate() {
      this.controls.update()
      TWEEN.update()
      requestAnimationFrame( this.animate );
      this.renderer.render( this.scene, this.camera );
    }
  }
}
</script>

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值