three.js学习笔记2:物体变换和动画

在渲染出来一个物体之后,就可以尝试将他进行变换和动画了

变换transform

物体的变换主要有三个方面:position位置变换,scale缩放变换和rotation旋转变换

position

position对象继承自Vector3类,vector(矢量),用这个类来定义空间中物体的位置信息

position的属性x,y,z是这个物体在坐标轴的位置mesh.position.x=0.7

position设置物体位置的简写形式:mesh.position.set(x,y,z)

position.length()方法是物体到场景中心的距离:mesh.position.length()

position.distanceTo(一个Vector3类或另一个物体)方法是两个物体之间的距离:mesh.position.distanceTo(new THREE.Vector3(0,1,2))mesh.position.distanceTo(camera.position)

position.normalize()方法是把物体的向量归一化,也就是把他到场景中心的向量长度减小到1,但是向量方向不发生变化:mesh.position.normalize(),此时,mesh.position.length()就为1了

scale

缩放就很简单了 x轴方向拉长2倍:mesh.scale.x=2 同样设置缩放也有简写形式:mesh.scale.set(2,0.5,0.5)

rotation

旋转有两种形式,一种是rotation,一种是quaternion,quaternion在three.js journey中并没有过多涉及,但是rotation变化同样或导致quaternion变化,两者是同步的

旋转也涉及x,y,z轴,可以把轴想象成一根穿过物体中心的棍子,固定物体的旋转方向,旋转值是用弧度表示的,也就是说,旋转180°是3.14159...,也可以用原生javascript的Math.PI表示mesh.rotation.y=Math.PI/2

这里要注意,当物体沿着一个轴旋转的时候,可能除了固定物体的轴之外的其他轴之外其他轴也被旋转了,当有多个方向的旋转时,默认的旋转顺序是x,y,z

如果在旋转过程中,想要沿一个轴旋转却并没有成功,那么这个轴叫做"万象节锁",解决万向节锁的方法是改变旋转轴的先后顺序,使用reorder()方法

ini 复制代码
mesh.rotation.reorder("YXZ")
mesh.rotation.y=Math.PI*0.25
mesh.rotation.x=Math.PI*0.25

可以添加上reorder()方法,然后再去掉对比,会发现最终物体的方向是不同的,这就是因为物体旋转的先后顺序不同导致的

相机的lookAt()

相机看向的方向默认是场景的中心,那么,如果想要让相机看向某个物体或者某个坐标点呢?这就要用到lookAt()方法了 camera.lookAt(mesh.position)或者camera.lookAt(new THREE.Vector3(3,0,0))

组group

在3d场景中,如果想要让一群物体整体向右移动或者整个放大,一个一个操作未免有些复杂,也不一定能控制到一样的程度,这就需要将这些物体放在一个组内,成为一个组内的成员

创建一个组:const group=new THREE.Group() 把组放在场景中:scene.add(group) 创建一些组内的成员:const cube1=new THREE.Mesh(形状,材质) 把组内成员放进组里:group.add(cube1) 这时,若要整体变换组内所有成员,可以对组进行变换:group.rotation.z=Math.PI*0.25

动画animation

先说一下动画涉及的概念:fps是每秒的帧数,也叫帧率,每个电脑因为性能的不同,帧率也不同

而动画要用到一个原生js方法,window.requestAnimationFrame(函数)表示下一帧要执行的函数,也就是说动画的速度和帧相关,是每一帧动画做到什么行为

scss 复制代码
//动画函数
const tick=()=>{
    //物体每帧旋转弧度+0.01
    mesh.rotation.x +=0.01
    //每帧的变化都要渲染出来
    renderer.render(scene.camera)
    //下一帧要再次执行tick函数,让物体每帧都变化
    window.requestAnimationFrame(tick)
}
tick()

帧率不同就导致一个问题,我们期望的动画是不论在什么帧率下都有一样的动画速度,可是,如果我们要移动一个物体,如果每帧移动10cm,如果帧率很高,那么物体就会移动的很快,如果帧率很低,那么物体移动缓慢,物体变换的速度在每个电脑上很有可能是不一样的,那么人的体验也是不同的,所以,就需要做一些事情,让物体移动跟帧率无关,也就是让动画适应帧率

有两种方法,在下面会写出来:原生js方法和three.js的方法

使用原生js的方法

原生js使用Data.now()能获取当前的时间戳,所以,可以获取每帧的动画开始的时间戳,时间戳的差就是每帧之间的时间差,将动画的速度和每帧的时间差相关联,那么帧率高的帧与帧之间的时间差就短,一帧的时间内变换的就少,但是帧率高,在固定时间内其实是一样的变换效果

ini 复制代码
const time=Date.now()
const tick=()=>{
    const currentTime=Date.now();
    const deltaTime=currentTime-time;
    mesh.rotation.x +=0.01*deltaTime;
    renderer.render(scene,camera);
    window.requestAnimationFrame(tick)
}
tick()

使用three.js的方法

three.js中有一个方法能获得一个时间段 先实例化Clock类得到一个的对象:const clock=new THREE.Clock() 使用这个对象的getElapsedTime()方法:const elapsedTime=clock.getElapsedTime(),这里获取的时间是从实例化Clock到现在的时间,单位是秒,物体变换的值与事件有关,也就与帧率无关了

javascript 复制代码
const clock=new THREE.Clock()
const tick=()=>{
    //elapsedTime这里也就是一帧的时间
    const elapsedTime=clock.getElapsedTime()
    //这里应该是每秒旋转一圈
    mesh.rotation.y=elapsedTime*Math.PI*2
    renderer.render(scene.camera)
    window.requestAnimationFrame(tick)
}
tick()

注:Clock还有一个getDelta()方法,但是不要使用他,用以出问题

如果还想要有更多的控制,比如创建一个双胞胎物体,创建时间先等,可以使用gsap这个库 npm i gsap@3.5.1

gsap有自己的tick函数,他在内部自己执行请求任务,所以我们的tick只需要不停的渲染就可以

gsap.to()方法,参数:第一个是动画的物体的position,第二个参数是一个对象,在这个对象中有不同属性的目标 gsap.to(mesh.position,{x:2,duration:1,delay:1})

相关推荐
格瑞@_@3 天前
11.Three.js使用indexeddb前端缓存模型优化前端加载效率
前端·javascript·缓存·three.js·indexeddb缓存
谢小飞3 天前
我做了三把椅子原来纹理这样加载切换
前端·three.js
小白菜学前端3 天前
ThreeJS创建一个3D物体的基本流程
3d·three.js
茶老翁4 天前
1-初识Three.js
前端·three.js
莫石6 天前
搓绳子(直)
前端·数学·three.js
小白菜学前端6 天前
3d 添加辅助坐标器和轨道控制器
3d·three.js
孙_华鹏8 天前
threejs——实战中材质的应用
前端·three.js·数据可视化
天涯学馆11 天前
Three.js灯光阴影与动画交互
前端·unity3d·three.js
格瑞@_@15 天前
6.Three.js贴图与uv映射(uv坐标)理解和实践
javascript·three.js·贴图·uv
入秋丶24 天前
threejs - 包围盒和包围球
three.js