书接上回
这有个初级版本,包括一个简易的动画类和一个事件派发器:点击访问
完善
上一个派发器逻辑混乱书写潦草,所以我重新写了一个
import { EventDispatcher, Raycaster, Vector2, Vector3 } from "three";
// 事件管理器 用于处理事件交互
export class EventManage {
/**
*
* @param {HTMLElement} dom
* @param {Camera} camera
* @param {Scene} scene
*/
constructor(dom, camera, scene) {
this.mouse = new Vector2();
this.raycaster = new Raycaster();
this.dom = null;
this.camera = null;
this.scene = null;
this.last = null;
this.listener = [];
this.dom = dom;
this.camera = camera;
this.scene = scene;
let last = null;
let _lastinter = null;
let leftbutton = false;
let moved = false; // 判断移动参数
const mousemoveEvent = (e) => {
const intersection = this.captureIntersection(e)
let { object, _inter } = this.filterIntersection(intersection)
// 处理object
if (object) {
// 如果射线捕获到了mesh
if (last) {
// 如果缓存了上次捕获的mesh
if (last.uuid == object.uuid) {
// 如果last和object是同一个mesh,则表示鼠标移动
object.dispatchEvent({ type: "mousemove", point: _inter.point })
this.selfHandleCallbacksByType("mousemove", _inter)
} else {
// 如果last和object不是同一个 则表示last出,object进
last.dispatchEvent({ type: "mouseout", point: _inter.point })
this.selfHandleCallbacksByType("mouseout", _lastinter)
object.dispatchEvent({ type: "mousein", point: _inter.point })
this.selfHandleCallbacksByType("mousein", _inter)
}
} else {
// 如果没有last只有object 表示object进
object.dispatchEvent({ type: "mousein", point: _inter.point })
this.selfHandleCallbacksByType("mousein", _inter)
}
last = object//进出事件结束后 last缓存当前object
_lastinter = _inter
} else {
// 如果射线没有捕获到object
if (last) {
// 如果没有捕获到object 但有last缓存表示 last出
last.dispatchEvent({ type: "mouseout", point: _lastinter.point })
this.selfHandleCallbacksByType("mouseout", _lastinter)
// 释放内存
last = null
_lastinter = null
} else {
// 如果object和last都没有 则什么也不做 (不要删这个空状态 阅读用)
}
}
// 防止拖拽旋转后立即触发点击事件
if (leftbutton) {
moved = true;
}
}
const mousedownEvent = (e) => { // 阻止事件冒泡
const intersection = this.captureIntersection(e)
let { object, _inter } = this.filterIntersection(intersection)
if (object) {
object.dispatchEvent({
type: "mousedown",
point: _inter.point,
});
this.selfHandleCallbacksByType("mousedown", _inter)
}
// if (intersection.length > 0) {
// intersection[0].object.dispatchEvent({
// type: "mousedown",
// point: intersection[0].point,
// });
// }
if (e.button === 0) {
leftbutton = true;
}
}
const mouseupEvent = (e) => { // 阻止事件冒泡
const intersection = this.captureIntersection(e)
let { object, _inter } = this.filterIntersection(intersection)
if (object) {
object.dispatchEvent({
type: "mouseup",
point: _inter.point,
});
this.selfHandleCallbacksByType("mouseup", _inter)
}
if (e.button === 0) {
leftbutton = false;
}
}
const mouseclickEvent = (e) => {
// 阻止事件冒泡
const intersection = this.captureIntersection(e)
if (!moved) {
if (intersection.length > 0) {
intersection[0].object.dispatchEvent({
type: "click",
point: intersection[0].point,
});
}
let callbacks = this.listener.filter((item) => item.name == "click");
if (callbacks.length > 0) {
callbacks.forEach(({ callback }) =>
callback({
object: intersection[0],
})
);
}
}
moved = false;
}
this.windowEventList = [
{ name: 'mousemove', callback: mousemoveEvent },
{ name: 'mouseup', callback: mouseupEvent },
{ name: 'mousedown', callback: mousedownEvent },
{ name: 'click', callback: mouseclickEvent },
]
for (let i = 0; i < this.windowEventList.length; i++) {
let { name, callback } = this.windowEventList[i]
console.log('signWindowEvent', { name, callback });
this.signWindowEvent(name, callback)
}
}
// 刷新鼠标位置
captureIntersection(e) {
if (!this.camera) return
let { camera: _camera, dom, mouse, scene, raycaster } = this
mouse.x = (e.offsetX / dom.offsetWidth) * 2 - 1;
mouse.y = (-e.offsetY * 2) / dom.offsetHeight + 1;
raycaster.setFromCamera(mouse, _camera);
const intersection = raycaster.intersectObjects(scene.children);
return intersection
}
// 过滤出响应用的事件
filterIntersection(intersection) {
let object = null, _inter = null
// 这里用于筛选mesh 一般来说并不是所有物体都需要事件,比如装饰性的边线,背景,或者流水,玻璃,草丛等可穿透的东西
// 当然创建场景的时候要给mesh添加标记才能用
for (let i = 0; i < intersection.length; i++) {
let _obj = intersection[i].object
if (_obj.eventIgnore) continue
// if (!_obj.isPorMesh) continue
else {
_inter = intersection[i]
object = _obj; break
}
}
return { object, _inter }
}
// 执行特定种类的事件
selfHandleCallbacksByType(type, object) {
let callbacks = this.listener.filter((item) => item.name == type);
if (callbacks.length > 0) {
callbacks.forEach(({ callback, name }) =>
callback({
name: name,
object: object,
})
);
}
}
// 注册与卸载额外事件
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);
}
}
}
removeAllListener() {
for (let i = 0; i < this.listener.length; i++) {
if (listener[i].name) this.listener[i].name = null
if (listener[i].callback) this.listener[i].callback = null
}
this.listener = []
}
// 注册与卸载事件
signWindowEvent(name, callback) {
this.dom.addEventListener(name, callback)
}
disposeWindowEvent(name, callback) {
this.dom.removeEventListener(name, callback)
}
// 销毁
destroyed() {
for (let i = 0; i < this.windowEventList; i++) {
let { name, callback } = this.windowEventList[i]
this.disposeWindowEvent(name, callback)
}
this.removeAllListener()
}
}
使用
使用camera scene 和dom创建后,在模型上添加事件,也可以直接给事件派发器添加事件,其中transController是变换控制器,用于移动旋转拉伸物体,是threejs自带工具
const eventManage = new EventManage(dom, camera, scene);
//物体添加事件
mesh.addEventListener("click",(self)=>callback())
//直接给派发器添加事件
eventManage.addEventListener("click", function (e) {
if (e.object) {
let mesh = e.object.object;
if (mesh.isPorMesh) {
transController.attach(mesh);
console.log('click event', e);
} else {
transController.detach();
}
} else {
transController.detach();
}
});
/**鼠标移动 事件 */
eventManage.addEventListener("mousemove", function (e) {
if (e.object) {
let mesh = e.object.object;
if (mesh.isPorMesh) {
console.log('mousemove event', e);
}
} else {
}
});
/**鼠标移入 事件 */
eventManage.addEventListener("mousein", function (e) {
if (e.object) {
let mesh = e.object.object;
if (mesh.isPorMesh) {
console.log('mousein event', e);
} else {
}
} else {
}
});
/**鼠标移出 事件 */
eventManage.addEventListener("mouseout", function (e) {
if (e.object) {
let mesh = e.object.object;
if (mesh.isPorMesh) {
console.log('mouseout event', e);
} else {
}
} else {
}
});
/**鼠标按下 事件 */
eventManage.addEventListener("mousedown", function (e) {
if (e.object) {
let mesh = e.object.object;
if (mesh.isPorMesh) {
console.log('mousedown event', e);
} else {
}
} else {
}
});
/**鼠标抬起 事件 */
eventManage.addEventListener("mouseup", function (e) {
if (e.object) {
let mesh = e.object.object;
if (mesh.isPorMesh) {
console.log('mouseup event', e);
} else {
}
} else {
}
});