threejs为我们提供了强大的动画系统接口api,通过这些接口,我们可以很轻松的实现物体的移动、旋转、缩放、颜色变化、透明度变化等各种效果,今天我们就来了解下threejs中的动画系统。
首先我们先了解几个在threejs动画系统中比较重要的组件
keyframetrack 关键帧轨道
关键帧轨道(keyframetrack)是关键帧(keyframes)的定时序列, 它由时间和相关值的列表组成, 用来让一个对象的某个特定属性动起来。
keyframetrack中总是存在两个数组:times数组按顺序存储该轨道的所有关键帧的时间值,而values数组包含动画属性的相应更改值。
值数组中的每一个成员,属于某一特定时间点,不仅可以是一个简单的数字,还可以是一个向量(如果是位置动画)或者是一个四元数(如果是旋转动画)。 因此,值数组(也是一个平面阵列)的长度可能是时间数组的三四倍。
构造函数
keyframetrack( name : string, times : array, values : array, interpolation : constant )
name - 关键帧轨道(keyframetrack)的标识符
times - 关键帧的时间数组, 被内部转化为 float32array
values - 与时间数组中的时间点相关的值组成的数组, 被内部转化为 float32array
interpolation - 使用的插值类型
keyframetrack具体的属性和方法查看官方文档,这里不再赘述。
常用的keyframetrack子类
vectorkeyframetrack:向量类型的关键帧轨道
colorkeyframetrack:反应颜色变化的关键帧轨道
booleankeyframetrack:布尔类型的关键帧轨道
numberkeyframetrack:数字类型的关键帧轨道
quaternionkeyframetrack:四元数类型的关键帧轨道
stringkeyframetrack:字符串类型的关键帧轨道
animationclip 动画剪辑
动画剪辑(animationclip)是一个可重用的关键帧轨道集,它用来定义动画
构造函数
animationclip( name : string, duration : number, tracks : array )
name - 此剪辑的名称
duration - 持续时间 (单位秒). 如果传入负数, 持续时间将会从传入的数组中计算得到。
tracks - 一个由关键帧轨道(keyframetracks)组成的数组。animationclip里面,每个动画属性的数据都存储在一个单独的keyframetrack中
animation mixer 动画混合器
动画混合器是用于场景中特定对象的动画的播放器。当场景中的多个对象独立动画时,每个对象都可以使用同一个动画混合器。
构造函数
animationmixer( rootobject : object3d )
rootobject - 混合器播放的动画所属的对象
属性
.time : number类型;全局的混合器时间(单位秒; 混合器创建的时刻记作0时刻)
.timescale : number类型;全局时间(mixer time)的比例因子
注意: 将混合器的时间比例设为0, 稍后再设置为1,可以暂停/取消暂停由该混合器控制的所有动作。
常用方法
.clipaction (clip : animationclip, optionalroot : object3d) : animationaction
返回所传入的剪辑参数的animationaction, 根对象参数可选,默认值为混合器的默认根对象。第一个参数可以是动画剪辑(animationclip)对象或者动画剪辑的名称。
如果不存在符合传入的剪辑和根对象这两个参数的动作, 该方法将会创建一个。传入相同的参数多次调用将会返回同一个剪辑实例。
.update (deltatimeinseconds : number) : 推进混合器时间并更新动画
通常在渲染循环中完成, 传入按照混合器的时间比例(timescale)缩放过的clock.getdelta
animationaction 动画动作
animationactions 用来调控制存储在animationclips中的动画。通过配置animationaction,我们可以决定何时播放、暂停或停止其中一个混合器中的某个animationclip, 这个animationclip是否需要重复播放以及重复的频率, 是否需要使用淡入淡出或时间缩放,以及一些其他内容(例如交叉渐变和同步)。
构造函数
animationaction( mixer : animationmixer, clip : animationclip, localroot : object3d )
mixer - 被此动作控制的 动画混合器
clip - 动画剪辑 保存了此动作当中的动画数据
localroot - 动作执行的根对象
注意: 通常我们不直接调用这个构造函数,而是先用animationmixer.clipaction实例化一个animationaction,因为这个方法提供了缓存以提高性能。
动画实例
通过上面的介绍我们了解了threejs中动画系统的几个常用组件,下面我们通过创建一个移立方体,并使其通过threejs的动画系统移动、旋转、缩放、变色等操作来使其运动起来;
和前面章节一样,先搭建环境,代码如下,具体细节就不讲了,有备注,不了解的可以看下前面的文章
引入threejs,创建场景、相机、渲染器等
index.html中
<body>
<script type="importmap">
{
"imports":{
"three":"../../three.js/build/three.module.js",
"three/addons/": "../../three.js/examples/jsm/"
}
}
</script>
<script type="module" src="./index.js"></script>
</body>
index.js中代码
import * as three from 'three'
import { orbitcontrols } from 'three/addons/controls/orbitcontrols.js'
// 定义变量
let camera,scene,renderer
let axeshelper
let heslight,dirlight
let box
let controls
// 初始化场景
initscene()
// 初始化相机
initcamera()
// 初始化辅助轴
initaxeshelper()
// 初始化灯光
initlight()
// 初始化渲染器
initrenderer()
// 循环执行
animate()
// 初始化轨道控制器
initcontrol()
// 窗体重置
window.addeventlistener('resize', function () {
camera.aspect = window.innerwidth / window.innerheight
camera.updateprojectionmatrix()
renderer.setsize(window.innerwidth, window.innerheight)
})
function initscene() {
scene = new three.scene()
scene.background = new three.color(0x888888)
}
function initcamera() {
camera = new three.perspectivecamera(75,window.innerwidth / window.innerheight,0.1,100)
camera.position.set(5,5,5)
}
function initaxeshelper() {
axeshelper = new three.axeshelper(3)
scene.add(axeshelper)
}
function initlight() {
heslight = new three.hemispherelight()
heslight.intensity = 0.3
scene.add(heslight)
dirlight = new three.directionallight()
dirlight.position.set(5,5,-5)
scene.add(dirlight)
}
function initcontrol() {
controls = new orbitcontrols(camera, renderer.domelement)
}
function initrenderer() {
renderer = new three.webglrenderer({ antialias: true })
renderer.setpixelratio(window.devicepixelratio)
renderer.setsize(window.innerwidth,window.innerheight)
document.body.appendchild(renderer.domelement)
}
function animate() {
requestanimationframe(animate)
renderer.render(scene,camera)
}
创建立方体
// 初始化物体
initmeshes()
function initmeshes() {
box = new three.mesh(
new three.boxgeometry(1,1,1),
new three.meshlambertmaterial({color:0x00ff00})
)
scene.add(box)
}
创建动画
先创建一个initanimation()函数并调用该函数,将动画相关的内容写入该代码块
// 创建动画
initanimation()
function initanimation() {
}
创建移动动画
首先我们来创建移动动画,我们先来定义动画的关键帧,移动动画的关键帧我们用vectorkeyframetrack创建,在initanimation()中添加如下代码
创建movekeyframe 关键帧
// 移动
const movekeyframe = new three.vectorkeyframetrack(
'.position',//要控制关键帧的名称
[0,1,2],// 定义三帧
[
0,0,0,//第一帧位置
5,0,0,//第二帧位置
0,0,0//第三帧位置
]
)
定义变量clip 并创建动画剪辑
在index.js的顶部定义clip变量
let clip
在initanimation()中创建动画剪辑
// 动画剪辑
clip = new three.animationclip(
'action', //动画名称
4,//动画持续时间
[movekeyframe]//轨迹
)
上面两步我们分别创建了关键帧和动画剪辑,但是这两个部分是独立的,没有任何关联,我们需要将上面的关键帧和动画剪辑关联起来,这就要用到动画混合器了
创建动画混合器
在index.js的顶部定义mixer变量
let mixer
enableanimation()
创建enableanimation()函数,并在该函数中创建动画混合器的实例,该实例接收一个参数,将上面创建的box作为参数传入
function enableanimation() {
// 通过创建动画混合器实例,实现要做动画的物体与动画关联起来
mixer = new three.animationmixer( box )
}
执行动画混合器的clipaction()方法,该方法接收一个参数,将上面创建的clip作为参数传入
其返回所传入的剪辑参数的animationaction,定义一个变量clipaction 用于接收返回的animationaction
// 通过动画混合器的clipaction方法,实现动画剪辑animationclip与动画混合器的关联
const clipaction = mixer.clipaction( clip )
调用animationaction的play方法,执行动画
clipaction.play()
enableanimation()方法的完整代码如下
function enableanimation() {
// 通过创建动画混合器实例,实现要做动画的物体与动画关联起来
mixer = new three.animationmixer( box )
// 通过动画混合器的clipaction方法,实现动画剪辑animationclip与动画混合器的关联
const clipaction = mixer.clipaction( clip )
// 通过上面两步实现 box和clip的关联
clipaction.play()
}
通过上面的代码,我们已经完成了关键帧定义、动画剪辑创建、动画混合器创建和执行动画的代码,但是,刷新浏览器发现还没有动画过程,这是因为我们还需要将动画混合器在周期处理函数中调用update函数进行更新
在执行update函数时,其接收一个deltatimeinseconds 参数,我们先创建一个threejs内置的时钟对象
let clock = new three.clock()
在animate()方法中定义变量delta 用来接收clock的getdelta()方法返回值,其返回的是自时钟创建开始到现在流失的时间
const delta = clock.getdelta() //获取自 .oldtime 设置后到当前的秒数。
将delta 作为参数传给动画混合器的update方法
// 更新mixer,delta 一个时间的概念
mixer.update(delta)
animate()方法中的完整代码如下
function animate() {
// 获取流失的时间delta
const delta = clock.getdelta() //获取自 .oldtime 设置后到当前的秒数。
requestanimationframe(animate)
// 更新mixer,delta 一个时间的概念
mixer.update(delta)
renderer.render(scene,camera)
}
至此,我们就实现了物体的移动动画,刷新浏览器,查看效果
旋转动画
要实现旋转动画,需要先定义沿着哪个轴旋转,并定义旋转的起始角度和终止角度,然后在通过quaternionkeyframetrack四元数类型的关键帧轨道来定义关键帧,代码如下
// 旋转
const xaxis = new three.vector3(1,0,0) //三维向量,沿x轴
const qinitial = new three.quaternion().setfromaxisangle(xaxis,0)//起点角度
const qfinal = new three.quaternion().setfromaxisangle(xaxis,math.pi)//终点角度
const rotationkeyframe = new three.quaternionkeyframetrack(
'.quaternion',
[0,1,2],//三帧
[
qinitial.x,qinitial.y,qinitial.z,qinitial.w,//第一帧
qfinal.x,qfinal.y,qfinal.z,qfinal.w,//第二帧
qinitial.x,qinitial.y,qinitial.z,qinitial.w//第三帧
]
)
定义好关键帧后,将上面定义的关键帧添加到animationclip中
// 动画剪辑
clip = new three.animationclip(
'action', //动画名称
4,//动画持续时间
[movekeyframe,rotationkeyframe]//轨迹
)
刷新浏览器看效果,现在立方体即旋转又移动
同样的方法,我们可以添加缩放和颜色变化,具体跟上面代码相似,就不在啰嗦了。
ok,这次就写到这里,喜欢的点赞关注收藏哦
发表评论