简介
上一篇介绍了Three.js中的主要框架和基础内容并实现了一个简单的Demo,本篇文章会介绍一些简单的优化和辅助工具,包括自带的和使用广泛的工具。
Resize
先解决一个小问题,打开上次的Demo,尝试拖拽一下浏览器,是不是立方体还在原来的位置没有变化?
加上一个监听函数,在浏览器尺寸变化时,获取相应的宽高再更新相机和渲染器的相关参数即可。
arduino
window.addEventListener('resize', () => {
// Update Sizes
sizes.width = window.innerWidth
sizes.height = window.innerHeight
// Update Camera
camera.aspect = sizes.width / sizes.height
camera.updateProjectionMatrix()
// Update Renderer
renderer.setSize(sizes.width, sizes.height)
})
OrbitControls
先让立方体停止旋转,此时只能观测到一个面。
arduino
// mesh.rotation.x += 0.01;
// mesh.rotation.y += 0.01;
问题来了,如果不希望改动立方体,有什么办法可以观察到它的全貌呢?
很简单,观测物体不动,移动观测点位(相机)就可以了,根据鼠标的移动位置改变相机的position
值即可简单模拟。
这里涉及到一些计算相关的问题。
首先,有一个知识点需要大家了解一下,Three.js 的坐标体系和浏览器是不同的。在浏览器中基准点在屏幕左上角 的位置,随向右向下 逐渐增大,而 Three.js 世界中,默认的中心点在正中心 ,随向上向右增大,反方向减小。
因此需要对坐标数据进行一次转化,代码见示例,最终将 cursor 的值限定在了[-0.5, 0.5]
之间,因为中心向左向下时,取负值更容易计算,至于 0.5 是个人习惯取值,可以自由设定。
arduino
// 定义鼠标的指针值
const cursor = { x: 0, y: 0 }
// 鼠标移动的时候改变 cursor 值
window.addEventListener('mousemove', (e) => {
cursor.x = e.clientX / sizes.width - 0.5
cursor.y = -(e.clientY / sizes.height - 0.5)
})
第二个问题,相机如何移动才合适呢?
这个例子比较简单,只考虑XOZ平面的移动。
思考一件事情,当物体在地面上不动时,想要拍摄它的前后左右,并且拍摄出的物体大小尽量保持一致,应该怎么拍呢,是不是以它为圆心,按固定的半径绕一圈勒。
arduino
// tick 函数中
const radius = 3
camera.position.x = Math.sin(cursor.x * Math.PI * 2) * radius
camera.position.z = Math.cos(cursor.x * Math.PI * 2) * radius
其实做法也是类似的,大概解释一下里面的一些参数吧:
- 一个数学小知识,忘记的同学刚好补一补课:
sin(x)² + cos(x)² = 1
,使用两个Math方法可以确定按圆周运动。 - radius 用于确定旋转半径。
- 三角函数内部的值是计算频率的,由于
cursor.x
取值范围在[-0.5, 0.5]
,再乘上Math.PI * 2
,可以理解为从屏幕最左端到最右端刚好转了一周,这个是可以自由调整的。
对了,相机观测视角是固定的,要记得实时调整它的观测点位哦
arduino
// tick 函数中
camera.lookAt(mesh.position)
至于Y的值大致设置一个范围吧,有兴趣的同学可以自行尝试,大致效果是这样的。
如果想观测物体,完全靠自己写一套其实比较麻烦,Three.js 提供了一个比较方便的工具OrbitControls
它的使用方式也很简单
javascript
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
// 需要传入相机和 canvas 节点
const controls = new OrbitControls(camera, canvas)
是的就是这么简单的两行代码,就可以360度拖动了。介绍一个比较常用的参数吧
ini
controls.enableDamping = true
这个参数可以开启控制器的阻尼,也就是惯性,具体效果可以自行尝试一下,但是开启了之后必须在每一帧更新一下控制器哦
scss
// 如果使用了 damping,必须要在 tick 函数中更新
controls.update()
AxesHelper
在开发的过程中,XYZ轴的显示有时候还是挺有必要的。
csharp
const axeshelper = new THREE.AxesHelper(3)
scene.add(axeshelper)
Clock
目前Demo的动画是使用requestAnimationFrame
来做时间控制的,但是了解这个函数的同学都知道它的时间实际上是不一定准确的,它可能被很多因素影响。
mesh.rotation.x += 0.01
因此动画函数中做上述操作时,时间间隔可能是不同的,有可能存在旋转卡顿或者旋转不匀速的情况,这时候可以使用官方提供的时间类工具Clock
,使用方式也很简单。
arduino
const clock = new THREE.Clock()
clock
中存有一个elapsedTime
,它的数值为当前时间减去clock
创建时间,这是一个匀速增长的数值,可以直接使用
ini
mesh.rotation.x = clock.getElapsedTime()
它会帮助我们更准确的去控制一些变换的操作。
Dat.gui
Three.js 的调试比较麻烦,因为它的属性和方法还有一些事件都并不和DOM挂钩很难去,我们可以借助 Dat.gui 这个UI调试库。它会提供一个面板,在里面进行输入,选择,点击等操作去更新属性或者调用方法。
基础用法大致有以下几种:
csharp
import * as dat from 'dat.gui'
// 初始化
const gui = new dat.GUI({ width: 400 })
// 设置一些全局参数
const parameters = {
color: 0xff0000,
goUp: () => mesh.position.y += 0.1,
}
// 普通设值
gui.add(mesh.position, 'x', -3, 3, 0.01).name('hello')
// 链式设值
gui.add(mesh.position, 'z').min(-3).max(3).step(0.01)
// 设置单选框
gui.add(mesh, 'visible')
// 设置颜色,这里要注意,点击颜色后要手动更新一下 material 的颜色
gui.addColor(parameters, 'color').onChange(() => material.color.set(parameters.color))
// 点击触发函数
gui.add(parameters, 'goUp')
总结
本文介绍了几种比较通用的工具,给视图和动画显示以及本地调试带来了一些帮助,并介绍了一些基础且使用较多的参数,更多参数方法大家去相应的文档中查看,Three.js 的官方文档 中介绍也很详细。本文相关的代码在 这里。