Threejs进阶之十二:Threejs与Tween.js结合创建动画

news2024/10/1 5:40:36

tween.js介绍

Tween.js是一个可以产生平滑动画效果的js库,其官方地址为: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()方法

控制补间的运动形式及方向 .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 (前半段加速,后半段减速)
常见的缓动动画如下
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实现物体颜色的变化、透明度的变化等,具体各位小伙伴可以自己摸索。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/485197.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

配置KylinV10

配置KylinV10 文章目录 配置KylinV10设置“root”登录密码允许通过图像界面登录到“root”开机自动登录到“root”关闭“麒麟安全授权认证”关闭自动睡眠挂载“Windows”下共享目录到虚拟机安装“Docker”到“KylinV10”B/S安装“Maven-3.6.3”安装“Gradle-4.4.1”安装“Jdk-8…

UG NX二次开发(C++)-建模-创建基准坐标系(NXOpen方法)

文章目录 1、前言2、UG NX中根据菜单来创建基准坐标系2.1 打开UG NX2.2 打开基准坐标系创建界面2.3 根据两个轴和原点创建基准坐标系 3、采用NXOpen方法来创建基准坐标系3.1 创建创建基准坐标系的方法3.2 在do_it方法中添加调用代码3.3 生成dll&#xff0c;并用NXOpen执行来测试…

spring boot原理分析

总体流程 prepareEnvironment里会生成基本的propertySource列表&#xff0c;当然后续还可能会改&#xff0c;比如apollo会在refreshContext时添加自己的propertySource。 prepareContext里会调initializer初始化ApplicationContext&#xff0c;接着加载bean定义。 refreshCo…

深入源码理解redis数据结构(一)

文章目录 一. 动态字符串SDS二. IntSet三. Dict 一. 动态字符串SDS 我们都知道Redis中保存的Key是字符串&#xff0c;value往往是字符串或者字符串的集合。可见字符串是Redis中最常用的一种数据结构。不过Redis没有直接使用C语言的字符串&#xff0c;因为C语言字符串存在着很多…

LVS负载均衡集群--DR模式

一、LVS-DR集群介绍 LVS-DR&#xff08;Linux Virtual Server Director Server&#xff09;工作模式&#xff0c;是生产环境中最常用的一 种工作模式。 1、LVS-DR 工作原理 LVS-DR 模式&#xff0c;Director Server 作为群集的访问入口&#xff0c;不作为网关使用&#xff0…

《程序员面试金典(第6版)》面试题 16.11. 跳水板

题目描述 你正在使用一堆木板建造跳水板。有两种类型的木板&#xff0c;其中长度较短的木板长度为shorter&#xff0c;长度较长的木板长度为longer。你必须正好使用k块木板。编写一个方法&#xff0c;生成跳水板所有可能的长度。 返回的长度需要从小到大排列。 示例 1 输入&a…

Request和Response应用

ServletRequest应用 ServletRequest应用非常广泛&#xff0c;下面是一些例子&#xff1a; 获取请求参数&#xff1a;可以使用HttpServletRequest的getParameter()方法获取请求参数。 获取请求头信息&#xff1a;可以使用HttpServletRequest的getHeader()方法获取请求头信息。…

数据结构(六)—— 二叉树(3)

文章目录 题1 589 N 叉树的前序遍历2 226 翻转二叉树递归迭代 3 101 对称二叉树递归迭代 4 104 二叉树的最大深度层序遍历直接解决递归 5 111 二叉树的最小深度层序遍历递归 6 222 完全二叉树的节点个数递归遍历 7 110 平衡二叉树递归 题 递归三部曲 1、确定递归函数的参数和返…

如何使用 PyTorch 进行半精度、混(合)精度训练

https://featurize.cn/notebooks/368cbc81-2b27-4036-98a1-d77589b1f0c4 nvidia深度学习加速库apex简单介绍 NVIDIA深度学习加速库Apex是一个用于PyTorch的开源混合精度训练工具包&#xff0c;旨在加速训练并减少内存使用。Apex提供了许多用于混合精度训练的工具&#xff0c;…

【Python基础入门学习】Python函数与变量的使用

python语法 1. 函数的快速体验2. 函数的基本使用2.1 函数的定义2.2 函数的调用2.3 第一个函数演练2.4 PyCharm 的调试工具2.5 函数的文档注释 3. 函数的参数3.1 函数参数的使用3.2 函数参数的作用3.3 形参和实参 4. 函数的返回值5. 函数的嵌套使用6 使用模块中的函数6.1 第一个…

码出高效:Java开发手册笔记(线程池及其源码)

码出高效&#xff1a;Java开发手册笔记&#xff08;线程池及其源码&#xff09; 码出高效&#xff1a;Java开发手册笔记&#xff08;线程池及其源码&#xff09; 码出高效&#xff1a;Java开发手册笔记&#xff08;线程池及其源码&#xff09;前言一、线程池的作用线程的生命周…

剑指 Offer:003 前 n 个数字二进制中 1 的个数

题目&#xff1a; 给定一个非负整数 n&#xff0c;请计算 0 到 n 之间的每个数字的二进制表示中 1 的个数&#xff0c;并输出一个数组 示例&#xff1a; 1、 输入: n 2 输出: [0,1,1] 解释: 0 --> 0 1 --> 1 2 --> 10 2、 输入: n 5 输出: [0,1,1,2,1,2] 解释: 0 …

第2关:用flex生成PL语言的词法分析器

任务描述 经过上个任务的磨砺&#xff0c;相信大家已经熟悉了lex/flex的使用。这一次我们将利用flex工具生成PL语言的词法分析器&#xff0c;要求输入一个PL语言源程序文件demo.pl&#xff0c;输出一个文件tokens.txt&#xff0c;该文件包括每一个单词及其种别枚举值&#xff0…

【五一创作】Qt quick基础1(包含基本元素Text Image Rectangle的使用)

Qt quick基础1&#xff08;包含基本元素Text Image Rectangle的使用&#xff09; 目录 Qt quick基础1&#xff08;包含基本元素Text Image Rectangle的使用&#xff09;前言qt中有直接设计ui的拖拽式的widget&#xff0c;为什么还需要Qtquick?QML语言Qt 版本创建一个Qt quick项…

两分钟学会 制作自己的浏览器 —— 并将 ChatGPT 接入

前期回顾 分享24个强大的HTML属性 —— 建议每位前端工程师都应该掌握_0.活在风浪里的博客-CSDN博客2分享4个HTML5 属性&#xff0c;开发必备https://blog.csdn.net/m0_57904695/article/details/130465836?spm1001.2014.3001.5501 &#x1f44d; 本文专栏&#xff1a;开发…

一文解决MySQL突击面试,关键知识点总结

文章目录 MySQL重要知识点回顾一、索引1. 为什么需要索引2. 索引的结构3. 避免索引失效3.1 联合索引不满足最左匹配原则3.2 隐式转换3.3 like查询3.4 索引列存在运算或者使用函数3.5 优化器 4. 执行计划4.1 type4.2 key4.3 rows4.4 extra 5. 建立索引5.1 什么情况下应该建索引&…

【Unity入门】24.碰撞检测

【Unity入门】碰撞检测 大家好&#xff0c;我是Lampard~~ 欢迎来到Unity入门系列博客&#xff0c;所学知识来自B站阿发老师~感谢 &#xff08;一&#xff09;碰撞体 &#xff08;1&#xff09;Collider组件 上节课我们有学习到&#xff0c;unity的物理系统提供了更方便的碰撞…

SPSS如何制作基本统计分析报表之案例实训?

文章目录 0.引言1.制作在线分析处理报告2.制作个案摘要报告3.制作行形式摘要报告4.制作列形式摘要报告 0.引言 因科研等多场景需要进行绘图处理&#xff0c;笔者对SPSS进行了学习&#xff0c;本文通过《SPSS统计分析从入门到精通》及其配套素材结合网上相关资料进行学习笔记总结…

全注解下的SpringIoc 续5-Bean的作用域

Bean的作用域主要有以下5种&#xff1a; 因为globalSession的作用域实践中基本不使用&#xff0c;所以这里就不对其过多介绍了。 另外application的作用域也完全可以用singleton作用域来代替&#xff0c;所以这里也不对其过多介绍了。 所以&#xff0c;我们主要看看singleton、…

Java——IO:输入输出流技术

简介 在java中&#xff0c;数据的输入输出都是以流的方式来处理。 流&#xff08;Stream&#xff09;&#xff0c;是一个抽象的概念&#xff0c;是指一连串的数据&#xff08;字符或字节&#xff09;&#xff0c;是以先进先出的方式发送信息的通道。 JDK中与输入/输出有关的…