【Threejs】添加鼠标事件到场景中的物体上,更完善的事件派发器

书接上回

这有个初级版本,包括一个简易的动画类和一个事件派发器:点击访问

完善

上一个派发器逻辑混乱书写潦草,所以我重新写了一个

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 {
            
        }
    });

鼠标事件效果展示

移动鼠标

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鸢_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值