自定義控件
回到PerspectiveCamera透視相機(jī)上來任连。注釋OrthographicCamera正交相機(jī),取消注釋PerspectiveCamera透視相機(jī)茵休,移動相機(jī)曹鸠,使其面向立方體金砍,刪掉tick中的對網(wǎng)格體旋轉(zhuǎn)的部分。
// 相機(jī)
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)
以用鼠標(biāo)控制相機(jī)為例餐蔬,首先需要知道鼠標(biāo)坐標(biāo)碎紊∮痈剑可以使用原生JavaScript的偵聽事件addEventListener,來偵聽mousemove事件仗考。
坐標(biāo)將通過回調(diào)函數(shù)返回音同,比如event.clientX和event.clientY
// 鼠標(biāo)
window.addEventListener('mousemove', (event) =>
{
? ? console.log(event.clientX, event.clientY)
})
此處的返回值可以直接使用,但是調(diào)整下值的范圍會更好秃嗜,比如調(diào)整為區(qū)間為1的值权均。
比如,對于x:
? ? 當(dāng)光標(biāo)位于畫布左側(cè)锅锨,輸出-0.5
? ? 當(dāng)光標(biāo)位于畫布中間叽赊,輸出0
? ? 當(dāng)光標(biāo)位于畫布右側(cè),輸出0.5
當(dāng)然也可以采用其它值必搞。
仿照size變量必指,這里創(chuàng)建個cursor變量,變量默認(rèn)屬性包含x和y恕洲,當(dāng)觸發(fā)mousemove回調(diào)時塔橡,更新屬性的值
// 鼠標(biāo)
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
這樣葛家,就完成了鼠標(biāo)位置的存儲,并且可以通過tick更新位置庶诡。
const tick = () =>
{
? ? // ...
? ? // 更新相機(jī)
? ? camera.position.x = cursor.x
? ? camera.position.y = cursor.y
? ? // ...
}
運(yùn)行后惦银,會發(fā)現(xiàn)沿y軸運(yùn)動方向不對,這是因?yàn)門hree.js中positon.y向上運(yùn)動時為正末誓,在網(wǎng)頁中clientY向下運(yùn)動時為正扯俱。
通過在cursor.y上添加負(fù)號,來糾正喇澡。
window.addEventListener('mousemove', (event) =>
{
? ? cursor.x = event.clientX / sizes.width - 0.5
? ? cursor.y = - (event.clientY / sizes.height - 0.5)
})
可以通過cursor.x和cursor.y乘上一個數(shù)字迅栅,來調(diào)整幅度大小。此時仍可通過lookAt()方法調(diào)整相機(jī)朝向晴玖。
const tick = () =>
{
? ? // ...
? ? // 更新相機(jī)
? ? camera.position.x = cursor.x * 5
? ? camera.position.y = cursor.y * 5
? ? camera.lookAt(mesh.position)
? ? // ...
}
更進(jìn)一步读存,還可以通過使用Math.sin()和Math.cos()實(shí)現(xiàn)相機(jī)繞網(wǎng)格體旋轉(zhuǎn)。
同時使用cos和sin時呕屎,可以讓運(yùn)動變成圓周運(yùn)動让簿。要運(yùn)動成完成的圓,角度的大小必須是Π的2倍秀睛《保可以通過使用Math.PI,使用Π的值蹂安。
要增加圓的半徑椭迎,可以簡單的將Math.sin()和Math.cos()結(jié)果乘上一個數(shù)锐帜。
const tick = () =>
{
? ? // ...
? ? // 更新相機(jī)
? ? 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內(nèi)置了許多控件類畜号,用來進(jìn)行許多的相似操作缴阎。
內(nèi)建控件
如果在Three.js文檔中搜索控件,會看到很多現(xiàn)成的控件简软。
DeviceOrientationControls設(shè)備陀螺儀控件
DeviceOrientationControls設(shè)備陀螺儀控件蛮拔,就是根據(jù)權(quán)限許可,依照設(shè)備方向相應(yīng)的旋轉(zhuǎn)相機(jī)痹升。如果設(shè)備合適语泽,可以用來創(chuàng)建全景圖或者VR效果。
FlyControls飛行控件
FlyControls飛行控件允許像飛船一樣控制相機(jī)视卢,此時可在三個軸上任意旋轉(zhuǎn)踱卵、并可前后移動。
FirstPersonControls第一人稱控件
FirstPersonControls第一人稱控件類似上一個据过,但具有一個固定向上的軸惋砂,即不能向前后左右翻轉(zhuǎn),雖然名字里有第一人稱绳锅,但是相機(jī)本身不含人西饵。
PointerLockControls指針鎖定控件
PointerLockControls指針鎖定控件使用了JavaScript的pointer lock API。通過這個鳞芙,隱藏了鼠標(biāo)指針眷柔,并使其居中,但仍在事件回調(diào)中繼續(xù)發(fā)送移動原朝。使用這個API驯嘱,可以創(chuàng)建FPS游戲,但是喳坠,這個API只提供了相機(jī)的旋轉(zhuǎn)鞠评,相機(jī)的移動和游戲中的物理效果仍舊需要自行處理。
OrbitControls環(huán)繞控件
OrbitControls環(huán)繞控件類似于上面的自定義控件的例子壕鹉√昊希可以通過左鍵繞某個點(diǎn)旋轉(zhuǎn),使用鼠標(biāo)右鍵橫向平移晾浴,使用滾輪放大縮小负乡。
TrackballControls軌跡球控件
TrackballControls軌跡球控件類似OrbitControls環(huán)繞控件,但是可以在豎直方向上翻轉(zhuǎn)脊凰。
TransformControls變換控件
TransformControls變換控件和相機(jī)無關(guān)抖棘。可以用它給對象添加一個控件,用來移動對象钉答。
這里只講OrbitControls環(huán)繞控件
OrbitControls環(huán)繞控件
注釋掉tick中更新相機(jī)的部分。
實(shí)例化
首先杈抢,使用OrbitControls類實(shí)例化變量数尿。
由于使用了Webpack,將會以以下形式引入
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
之后就可以直接實(shí)例化OrbitControls 類惶楼,但要在創(chuàng)建相機(jī)后執(zhí)行右蹦。
要使其正常工作,需要向其傳入相機(jī)和畫布歼捐,然后實(shí)例會自己處理鼠標(biāo)事件何陆。
// 控件
const controls = new OrbitControls(camera, canvas)
這樣就可以使用鼠標(biāo)左鍵或者右鍵來移動相機(jī),使用滾輪進(jìn)行縮放豹储。
比自定義控件方便得多贷盲,且?guī)в性S多控件。然后再往更深層次研究剥扣。
target目標(biāo)
默認(rèn)情況下巩剖,相機(jī)看向場景中心∧魄樱可以通過target目標(biāo)屬性修改它佳魔。
這個屬性是個Vector3,意思就是通過x晦炊、y鞠鲜、z三個屬性定義的。
如果希望OrbitControls環(huán)繞控件默認(rèn)查看立方體上方断国,只需要增加y值贤姆。
controls.target.y = 2
這個用處不是很大,先注釋掉它稳衬。
Damping阻尼
Damping阻尼通過加速度和摩擦力公式來平滑動畫庐氮。
要啟用阻尼,請將controls的enableDamping屬性設(shè)置為true宋彼。
為了正常運(yùn)行弄砍,還需要通過在每幀調(diào)用controls.update()∈涮椋可以通過tick()實(shí)現(xiàn)音婶。
// 控件
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true
// ...
const tick = () =>
{
? ? // ...
? ? // Update controls
? ? controls.update()
? ? // ...
}
這樣控件看起來更加流暢。
阻尼可以用在各種旋轉(zhuǎn)莱坎、移動衣式、縮放、角度控制上,甚至按鍵綁定上碴卧。
何時使用內(nèi)建控件
雖然這些控件很方便弱卡,但是都有局限性。如果過于依賴它們住册,可能需要以意想不到的方式改變類的工作方式婶博。
首先請確保所需的功能,然后檢查內(nèi)建控件的功能是否完全覆蓋所需的荧飞。
否則凡人,需要自己建立控件。