【threejs】事件处理eventDispatch(点击,滑动等)

构建一个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)
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

鸢_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值