构建一个class,传入dom scene和camera等必要参数即可新建,使用射线判断物体是否触发事件,一个场景可以建立多个事件处理
源码
import { EventDispatcher, Raycaster, Vector2 } from "three";
export class EventManage {//老版本继承EventDispatcher 可以让类获得dispatchEvent函数,但是新版本会报错
mouse = new Vector2()//鼠标位置
raycaster = new Raycaster()//射线
dom//threejs的容器
camera//相机
scene//场景 用于获取物体
last = null//记录上一个物体
/**
*
* @param {HTMLElement} dom
* @param {Camera} camera
* @param {Scene} scene
*/
constructor(
dom, camera, scene
) {
// super()
this.dom = dom
this.camera = camera
this.scene = scene
const mouse = this.mouse
const raycaster = this.raycaster
let last = null
dom.addEventListener('mousemove', (e) => {
// 获取鼠标相对canvas的位置在相机内的投影位置
mouse.x = e.offsetX / dom.offsetWidth * 2 - 1
mouse.y = -e.offsetY * 2 / dom.offsetHeight + 1
// 通过射线捕捉物体
raycaster.setFromCamera(mouse, camera);
const intersection = raycaster.intersectObjects(this.scene.children)//可以用任意3dboject(group..) 只要包含children就行
if (intersection.length > 0) {
// console.log(intersection[0]);
let object = intersection[0].object//对第一个物体添加事件 业务需要可以遍历intersection 寻找
object.dispatchEvent({
type: 'mousemove',
point: intersection[0].point
})
if (last && last.uuid == object.uuid) {
} else {
if (last)
last.dispatchEvent({
// 这个对象可以随便修改,只要带着type就行
type: 'mouseout',
point: intersection[0].point//我返回了点击位置的坐标
})
object.dispatchEvent({
type: 'mousein',
point: intersection[0].point
})
}
last = object//记录上一个物体做mousein mouseout判断
}
})
// dom.addEventListener('mouseup', (e) => {
// raycaster.setFromCamera(mouse, camera);
// const intersection = raycaster.intersectObjects(this.scene.children)
// if (intersection.length > 0) {
// // console.log(intersection[0]);
// intersection[0].object.dispatchEvent({
// type: 'mouseup',
// point: intersection[0].point
// })
// }
// })
// dom.addEventListener('mousedown', (e) => {
// raycaster.setFromCamera(mouse, camera);
// const intersection = raycaster.intersectObjects(this.scene.children)
// if (intersection.length > 0) {
// // console.log(intersection[0]);
// intersection[0].object.dispatchEvent({
// type: 'mousedown',
// point: intersection[0].point
// })
// }
// })
dom.addEventListener('click', (e) => {
raycaster.setFromCamera(mouse, camera);
const intersection = raycaster.intersectObjects(this.scene.children)
if (intersection.length > 0) {
// console.log(intersection[0]);
intersection[0].object.dispatchEvent({
type: 'click',
point: intersection[0].point
})
}
})
}
}
使用
import { EventManage } from '../EventManage'
this.eventManage = new EventManage($dom, this.camera, this.scene)
mesh.addEventListener('click', (e) => {
// // ractLabel
let { x, y } = e.point
...........do something
})
改进
由于threejs的旋转相机控制器的鼠标操作也会触发点击事件 所以给他加一个检测,若鼠标移动后点击不触发,不移动点击后才触发
import { EventDispatcher, Raycaster, Vector2 } from "three";
export class EventManage {
mouse = new Vector2()
raycaster = new Raycaster()
dom
camera
scene
last = null
listener = []
/**
*
* @param {HTMLElement} dom
* @param {Camera} camera
* @param {Scene} scene
*/
constructor(
dom, camera, scene
) {
// super()
this.dom = dom
this.camera = camera
this.scene = scene
const mouse = this.mouse
const raycaster = this.raycaster
let last = null
let leftbutton = false
let moved = false // 判断移动参数
dom.addEventListener('mousemove', (e) => {
mouse.x = e.offsetX / dom.offsetWidth * 2 - 1
mouse.y = -e.offsetY * 2 / dom.offsetHeight + 1
raycaster.setFromCamera(mouse, camera);
const intersection = raycaster.intersectObjects(this.scene.children)
if (intersection.length > 0) {
// console.log(intersection[0]);
let object = intersection[0].object
object.dispatchEvent({
type: 'mousemove',
point: intersection[0].point
})
if (last && last.uuid == object.uuid) {
} else {
if (last)
last.dispatchEvent({
type: 'mouseout',
point: intersection[0].point
})
object.dispatchEvent({
type: 'mousein',
point: intersection[0].point
})
}
last = object
}
let callbacks = this.listener.filter(item => item.name == 'mousemove')
if (callbacks.length > 0) {
callbacks.forEach(({ callback }) => callback(
{
object: intersection[0],
}
))
}
if (leftbutton) {
moved = true
}
})
dom.addEventListener('mouseup', (e) => {
raycaster.setFromCamera(mouse, camera);
const intersection = raycaster.intersectObjects(this.scene.children)
if (intersection.length > 0) {
// console.log(intersection[0]);
intersection[0].object.dispatchEvent({
type: 'mouseup',
point: intersection[0].point
})
}
// moved = false
if (e.button === 0) {
leftbutton = false
}
})
dom.addEventListener('mousedown', (e) => {
raycaster.setFromCamera(mouse, camera);
const intersection = raycaster.intersectObjects(this.scene.children)
if (intersection.length > 0) {
// console.log(intersection[0]);
intersection[0].object.dispatchEvent({
type: 'mousedown',
point: intersection[0].point
})
}
if (e.button === 0) {
leftbutton = true
}
})
dom.addEventListener('click', (e) => {
raycaster.setFromCamera(mouse, camera);
const intersection = raycaster.intersectObjects(this.scene.children)
if (intersection.length > 0) {
// console.log(intersection[0]);
intersection[0].object.dispatchEvent({
type: 'click',
point: intersection[0].point
})
}
if (!moved) {
let callbacks = this.listener.filter(item => item.name == 'click')
if (callbacks.length > 0) {
callbacks.forEach(({ callback }) => callback(
{
object: intersection[0],
}
))
}
}
moved = false
})
// setInterval(() => moved = false, 1)
}
addEventListener = (name, callback) => {
this.listener.push({ name, callback })
}
removeListener = (name_, callback_) => {
// let {name,callback} = this.listener
for (let i = 0; i < this.listener.length; i++) {
let { name, callback } = this.listener[i]
if (
name === name_ && callback === callback_
) {
this.listener.splice(i, 1)
}
}
}
}