自定义控件
回到PerspectiveCamera透视相机上来。注释OrthographicCamera正交相机,取消注释PerspectiveCamera透视相机,移动相机,使其面向立方体,删掉tick中的对网格体旋转的部分。
// 相机
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 1, 1000)
// const aspectRatio = sizes.width / sizes.height
// const camera = new THREE.OrthographicCamera(- 1 * aspectRatio, 1 * aspectRatio, 1, - 1, 0.1, 100)
// camera.position.x = 2
// camera.position.y = 2
camera.position.z = 3
camera.lookAt(mesh.position)
scene.add(camera)
以用鼠标控制相机为例,首先需要知道鼠标坐标。可以使用原生JavaScript的侦听事件addEventListener,来侦听mousemove事件。
坐标将通过回调函数返回,比如event.clientX和event.clientY
// 鼠标
window.addEventListener('mousemove', (event) =>
{
console.log(event.clientX, event.clientY)
})
此处的返回值可以直接使用,但是调整下值的范围会更好,比如调整为区间为1的值。
比如,对于x:
当光标位于画布左侧,输出-0.5
当光标位于画布中间,输出0
当光标位于画布右侧,输出0.5
当然也可以采用其它值。
仿照size变量,这里创建个cursor变量,变量默认属性包含x和y,当触发mousemove回调时,更新属性的值
// 鼠标
const cursor = {
x: 0,
y: 0
}
window.addEventListener('mousemove', (event) =>
{
cursor.x = event.clientX / sizes.width - 0.5
cursor.y = event.clientY / sizes.height - 0.5
console.log(cursor.x, cursor.y)
})
event.clientX除以sizes.width会返回一个介于0~1之间的值,通过减去一个0.5,让值分布在0.5~-0.5
这样,就完成了鼠标位置的存储,并且可以通过tick更新位置。
const tick = () =>
{
// ...
// 更新相机
camera.position.x = cursor.x
camera.position.y = cursor.y
// ...
}
运行后,会发现沿y轴运动方向不对,这是因为Three.js中positon.y向上运动时为正,在网页中clientY向下运动时为正。
通过在cursor.y上添加负号,来纠正。
window.addEventListener('mousemove', (event) =>
{
cursor.x = event.clientX / sizes.width - 0.5
cursor.y = - (event.clientY / sizes.height - 0.5)
})
可以通过cursor.x和cursor.y乘上一个数字,来调整幅度大小。此时仍可通过lookAt()方法调整相机朝向。
const tick = () =>
{
// ...
// 更新相机
camera.position.x = cursor.x * 5
camera.position.y = cursor.y * 5
camera.lookAt(mesh.position)
// ...
}
更进一步,还可以通过使用Math.sin()和Math.cos()实现相机绕网格体旋转。
同时使用cos和sin时,可以让运动变成圆周运动。要运动成完成的圆,角度的大小必须是Π的2倍。可以通过使用Math.PI,使用Π的值。
要增加圆的半径,可以简单的将Math.sin()和Math.cos()结果乘上一个数。
const tick = () =>
{
// ...
// 更新相机
camera.position.x = Math.sin(cursor.x * Math.PI * 2) * 2
camera.position.z = Math.cos(cursor.x * Math.PI * 2) * 2
camera.position.y = cursor.y * 3
camera.lookAt(mesh.position)
// ...
}
tick()
这只是一个自定义控制的例子,Three.js内置了许多控件类,用来进行许多的相似操作。
内建控件
如果在Three.js文档中搜索控件,会看到很多现成的控件。
DeviceOrientationControls设备陀螺仪控件
DeviceOrientationControls设备陀螺仪控件,就是根据权限许可,依照设备方向相应的旋转相机。如果设备合适,可以用来创建全景图或者VR效果。
FlyControls飞行控件
FlyControls飞行控件允许像飞船一样控制相机,此时可在三个轴上任意旋转、并可前后移动。
FirstPersonControls第一人称控件
FirstPersonControls第一人称控件类似上一个,但具有一个固定向上的轴,即不能向前后左右翻转,虽然名字里有第一人称,但是相机本身不含人。
PointerLockControls指针锁定控件
PointerLockControls指针锁定控件使用了JavaScript的pointer lock API。通过这个,隐藏了鼠标指针,并使其居中,但仍在事件回调中继续发送移动。使用这个API,可以创建FPS游戏,但是,这个API只提供了相机的旋转,相机的移动和游戏中的物理效果仍旧需要自行处理。
OrbitControls环绕控件
OrbitControls环绕控件类似于上面的自定义控件的例子。可以通过左键绕某个点旋转,使用鼠标右键横向平移,使用滚轮放大缩小。
TrackballControls轨迹球控件
TrackballControls轨迹球控件类似OrbitControls环绕控件,但是可以在竖直方向上翻转。
TransformControls变换控件
TransformControls变换控件和相机无关。可以用它给对象添加一个控件,用来移动对象。
这里只讲OrbitControls环绕控件
OrbitControls环绕控件
注释掉tick中更新相机的部分。
实例化
首先,使用OrbitControls类实例化变量。
由于使用了Webpack,将会以以下形式引入
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
之后就可以直接实例化OrbitControls 类,但要在创建相机后执行。
要使其正常工作,需要向其传入相机和画布,然后实例会自己处理鼠标事件。
// 控件
const controls = new OrbitControls(camera, canvas)
这样就可以使用鼠标左键或者右键来移动相机,使用滚轮进行缩放。
比自定义控件方便得多,且带有许多控件。然后再往更深层次研究。
target目标
默认情况下,相机看向场景中心。可以通过target目标属性修改它。
这个属性是个Vector3,意思就是通过x、y、z三个属性定义的。
如果希望OrbitControls环绕控件默认查看立方体上方,只需要增加y值。
controls.target.y = 2
这个用处不是很大,先注释掉它。
Damping阻尼
Damping阻尼通过加速度和摩擦力公式来平滑动画。
要启用阻尼,请将controls的enableDamping属性设置为true。
为了正常运行,还需要通过在每帧调用controls.update()。可以通过tick()实现。
// 控件
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true
// ...
const tick = () =>
{
// ...
// Update controls
controls.update()
// ...
}
这样控件看起来更加流畅。
阻尼可以用在各种旋转、移动、缩放、角度控制上,甚至按键绑定上。
何时使用内建控件
虽然这些控件很方便,但是都有局限性。如果过于依赖它们,可能需要以意想不到的方式改变类的工作方式。
首先请确保所需的功能,然后检查内建控件的功能是否完全覆盖所需的。
否则,需要自己建立控件。