上一節(jié)中我們實(shí)現(xiàn)了物體沿指定軌跡移動(dòng)的動(dòng)畫效果,這一節(jié)我們來(lái)實(shí)現(xiàn)讓模型移動(dòng)到鼠標(biāo)點(diǎn)擊的制定位置的動(dòng)畫效果。
先看下實(shí)現(xiàn)后的最終效果
要實(shí)現(xiàn)上面的動(dòng)畫效果,我們需要通過(guò)以下步驟來(lái)實(shí)現(xiàn)
第一步,監(jiān)聽鼠標(biāo)事件
我們需要監(jiān)聽鼠標(biāo)的點(diǎn)擊事件,獲取鼠標(biāo)點(diǎn)擊點(diǎn)相對(duì)瀏覽器可視區(qū)域左上角的距離,通過(guò)監(jiān)聽“pointerdown”事件,獲取點(diǎn)擊點(diǎn)的clientX和clientY;clientX/Y獲取到的是點(diǎn)擊點(diǎn)相對(duì)瀏覽器可視區(qū)域左上角距離。
renderer.domElement.addEventListener('pointerdown',function(event) {
event = event || window.event
initPos = {
x:event.clientX,
y:event.clientY
}
})
第二步:獲取鼠標(biāo)點(diǎn)擊點(diǎn)的屏幕坐標(biāo)
通過(guò)Element.getBoundingClientRect()中的 top屬性獲取元素上邊距離頁(yè)面上邊的距離,
通過(guò)left屬性獲取元素左邊距離頁(yè)面左邊的距離,
通過(guò)計(jì)算 clientX 與domElement.getBoundingClientRect().left的差值,獲取x點(diǎn)坐標(biāo);
通過(guò)計(jì)算 clientY 與domElement.getBoundingClientRect().top的差值,獲取y點(diǎn)坐標(biāo);
let x = event.clientX - renderer.domElement.getBoundingClientRect().left
let y = event.clientY - renderer.domElement.getBoundingClientRect().top
第三步:獲取畫布的寬度和高度并歸一化
通過(guò)坐標(biāo)轉(zhuǎn)換,將x和y的坐標(biāo)歸一化,即將屏幕坐標(biāo)轉(zhuǎn)換為threejs的世界坐標(biāo)
let canvasWidth = renderer.domElement.clientWidth
// clientWidth 返回元素的可視寬度,包括內(nèi)邊距,但不包括邊框、滾動(dòng)條或外邊距,以像素計(jì)
// canvas畫布高度
// clientHeight 返回元素的可視高度,包括內(nèi)邊距,但不包括邊框、滾動(dòng)條或外邊距,以像素計(jì)。
let canvasHeight = renderer.domElement.clientHeight
// offsetHeight 屬性返回元素的可視高度(以像素為單位),包括內(nèi)邊距、邊框和滾動(dòng)條,但不包括外邊距。
// offsetWidth 屬性返回元素的可視寬度(以像素為單位),包括內(nèi)邊距、邊框和滾動(dòng)條,但不包括外邊距。
// 坐標(biāo)轉(zhuǎn)換(歸一化的值)
const sx = -1 + (x / canvasWidth) * 2
const sy = 1 - (y / canvasHeight) * 2
第四步:創(chuàng)建光線投射器,并通過(guò)攝像機(jī)和鼠標(biāo)點(diǎn)擊的位置更新射線
// 光線投射器
const rayCaster = new THREE.Raycaster()
// 通過(guò)攝像機(jī)和鼠標(biāo)位置更新射線
rayCaster.setFromCamera(new THREE.Vector2(sx,sy),camera)
第五步:通過(guò)rayCaster.intersectObjects()方法檢測(cè)射線與物體的交集
這里我們是點(diǎn)擊在水面上,因此,將water作為參數(shù)傳入,檢測(cè)射線與水面的交集,將結(jié)果返回給intersects文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-484904.html
const intersects = rayCaster.intersectObjects([water])
第六步:判斷射線與物體是否有交集,如果有,獲取坐標(biāo)并處理邏輯
通過(guò)if語(yǔ)句判斷intersects.length是否大于零,如果大于零,說(shuō)明有交集,在if語(yǔ)句中處理如下邏輯:
1、通過(guò)intersects[0].point獲取鼠標(biāo)點(diǎn)擊時(shí)射線與water相交點(diǎn)的坐標(biāo)(新位置)存入newPos變量,
2、通過(guò)yacht.position.clone() 獲取模型當(dāng)前位置坐標(biāo)(老位置)存入originPos變量
3、通過(guò)camera.position.clone() 獲取相機(jī)當(dāng)前位置坐標(biāo)(老位置)存入cameraOriginPos 變量
4、通過(guò)向量減法獲取鼠標(biāo)點(diǎn)擊點(diǎn)的向量和向量長(zhǎng)度 存入vector 變量
5、創(chuàng)建一個(gè)四元數(shù)對(duì)象,通過(guò).setFromUnitVectors將該四元數(shù)設(shè)置為從方向向量new THREE.Vector3(0,0,-1)旋轉(zhuǎn)到方向向量 vector 單位向量 所需的旋轉(zhuǎn)角度。
6、創(chuàng)建一個(gè)Threejs的Clock()對(duì)象
7、創(chuàng)建一個(gè)speed常量為100,設(shè)置移動(dòng)速度
8、通過(guò)setInterval()方法啟動(dòng)定時(shí)器,通過(guò)speed和clock.getElapsedTime()失去時(shí)間的乘積,得到每次時(shí)間間隔移動(dòng)的長(zhǎng)度
9、通過(guò)每次時(shí)間間隔移動(dòng)的長(zhǎng)度除以向量總長(zhǎng)度,獲取每次間隔移動(dòng)的比值,將該比值與重點(diǎn)向量相乘,得到每次間隔移動(dòng)的向量坐標(biāo)
10、將上面得到的每次間隔移動(dòng)的向量坐標(biāo)與模型原始位置向量坐標(biāo)相加,得到模型每次時(shí)間間隔移動(dòng)的重點(diǎn)向量坐標(biāo)
11、將每次間隔移動(dòng)的終點(diǎn)坐標(biāo)復(fù)制給模型的position屬性
12、將每次間隔移動(dòng)的向量坐標(biāo)與相機(jī)原始坐標(biāo)相加得到每次時(shí)間間隔移動(dòng)的終點(diǎn)坐標(biāo),并將給坐標(biāo)復(fù)制給相機(jī)的position屬性
13、將每次時(shí)間間隔的終點(diǎn)坐標(biāo)movePos復(fù)制給控制器的target屬性,使其始終朝向movePos位置
14、執(zhí)行完成后清除定時(shí)器文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-484904.html
到了這里,關(guān)于Threejs進(jìn)階之十:讓模型移動(dòng)到鼠標(biāo)點(diǎn)擊的指定位置的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!