一个简单的threejs盒剖切功能

支持六面方向拖拽、反向、切面填充. 

 代码:
MouseHandler代码:
 

import {  Vector2 } from "three";
import * as THREE from "three";
import {EventDispatcher} from '../EventDispatcher'
type MouseEventTypeMap = {
    objectDragstart: {};
    objectDrag: {};
    objectDragend: { };
    dragstart: {};
    drag: {};
    dragend: {};
    objectLeave: {
        intersections: THREE.Intersection[];
    };
    objectEnter: {
        intersections: THREE.Intersection[];
    };
    pointerdown: {
        button: number; // 0 左键,1 中间键,2 右键
    };
    pointermove: {};
    pointerup: {};
    pointerupslow:{},
    select: {
        intersections: THREE.Intersection[];
    };
};
type CamelToSnakeCase<S extends string> = S extends `${infer T}${infer U}`
    ? `${T extends Capitalize<T> ? "-" : ""}${Lowercase<T>}${CamelToSnakeCase<U>}`
    : S;

type KeysToSnakeCase<T extends { [K: string]: any }> = {
    [Key in keyof T as CamelToSnakeCase<string & Key>]: T[Key];
};
export type MouseEventTypeMapCamel = KeysToSnakeCase<MouseEventTypeMap>;

type HoverCheckHandle = (
    nextIntersection: THREE.Intersection[],
    prevIntersection: THREE.Intersection[]
) => boolean;

enum POINTER_STATE{
    NONE=0,
    DOWN=1,
    OBJECT_DRAG=2,
    DRAG=3
}




export class MouseHandler extends EventDispatcher<MouseEventTypeMapCamel> {
    coord = new Vector2();
    pointDown = new Vector2();
    point = new Vector2();
    pointUp = new Vector2();
    pointDelta = new Vector2();
    pointOffset = new Vector2();
    pointLast = new Vector2();
    _rect?: DOMRect;
    type: string = "";
    pointerEvent?: PointerEvent;
    raycaster: THREE.Raycaster = new THREE.Raycaster();
    selectedIntersections: THREE.Intersection[] = [];
    hoverIntersections: THREE.Intersection[] = [];
    prevHoverIntersections: THREE.Intersection[] = [];
    selectObjects: THREE.Object3D[] = [];
    hoverObjects: THREE.Object3D[] = [];
    selectRecursive: boolean = false;
    hoverRecursive: boolean = false;
    enableRaycaster: boolean = true;
    enabled=true
    pointerState: number = 0;
    _onHoverCheck: HoverCheckHandle = (nextIntersection, prevIntersection) => {
        return nextIntersection[0].object!==prevIntersection[0]?.object
    };
    constructor(public domElement: HTMLElement,public win: Document|HTMLElement|Window,public _camera: THREE.Camera|(()=>THREE.Camera)) {
        super();
    }

    get camera(){
        return typeof this._camera==='function'?this._camera():this._camera;
    }
    get clientRect() {
        this._rect ??= this.domElement.getBoundingClientRect();
        return this._rect;
    }
    onHoverCheck(hoverCheckHandle: HoverCheckHandle) {
        this._onHoverCheck = hoverCheckHandle;
    }
    updateRaycaster(){
        this.raycaster.setFromCamera(this.coord, this.camera);
    }
    rayObjects(
        objects: THREE.Object3D[],
        recursive: boolean = false,
        intersections:THREE.Intersection[] = []
    ) {
        intersections.length = 0;
        this.updateRaycaster()
        this.raycaster.firstHitOnly=false
        this.raycaster.intersectObjects(objects, recursive, intersections);
        return intersections;
    }
    rayFirstObjects(
        objects: THREE.Object3D[],
        recursive: boolean = false,
        intersections:THREE.Intersection[] = []
    ) {
        intersections.length = 0;
        this.updateRaycaster()
        this.raycaster.firstHitOnly=true
        this.raycaster.intersectObjects(objects, recursive, intersections);
        return intersections;
    }
    rayObject(
        object: THREE.Object3D,
        recursive: boolean = false,
        intersections:THREE.Intersection[]  =[]
    ) {
        intersections.length = 0;
        this.updateRaycaster();
        this.raycaster.firstHitOnly=true
        this.raycaster.intersectObject(object, recursive,intersections);
        return intersections;
    }
    getMouse(e: MouseEvent, out: Vector2) {
        const x=e.pageX-window.scrollX
        const y=e.pageY-window.scrollY
        return out.set(
            x - this.clientRect.left,
            y - this.clientRect.top
        );
    }
    getCoord(point: Vector2, out: Vector2) {
        return out.set(
            (point.x / this.clientRect.width) * 2 - 1,
            (-point.y / this.clientRect.height) * 2 + 1
        );
    }
    handleAnimationMove=()=>{
        const e = this.pointerEvent!;
        this.getMouse(e, this.point)
        this.getCoord(this.point, this.coord)
        this.pointOffset.subVectors(this.point,this.pointDown)
        this.pointDelta.subVectors(this.point,this.pointLast)
        this.pointLast.copy(this.point)
        if(this.pointerState!==POINTER_STATE.NONE){
            if(this.selectedIntersections.length){
                if(this.pointerState===POINTER_STATE.DOWN){
                    this.pointerState=POINTER_STATE.OBJECT_DRAG
                    this.dispatchEvent({type:'object-dragstart'})
                }else{
                    this.dispatchEvent({type:'object-drag'})
                }   
            }else{
                if(this.pointerState===POINTER_STATE.DOWN){
                    this.pointerState=POINTER_STATE.DRAG
                    this.dispatchEvent({type:'dragstart'})
                }else{
                    this.dispatchEvent({type:'drag'})
                }   
            }
        }else{
            this.rayFirstObjects(this.getHoverObjects(),this.hoverRecursive,this.hoverIntersections)
            if(this.hoverIntersections.length){
                 if(this._onHoverCheck(this.hoverIntersections,this.prevHoverIntersections)){
                    if(this.prevHoverIntersections.length){
                        this.dispatchEvent({type:'object-leave',intersections:this.prevHoverIntersections.slice()})
                        this.prevHoverIntersections.length=0
                    }
                    this.dispatchEvent({type:'object-enter',intersections:this.hoverIntersections.slice()})
                    this.prevHoverIntersections=this.hoverIntersections.slice()
                 }
            }else{
                if(this.prevHoverIntersections.length){
                    this.dispatchEvent({type:'object-leave',intersections:this.prevHoverIntersections.slice()})
                    this.prevHoverIntersections.length=0
                }
            }
        }
        this.dispatchEvent({type:'pointermove'})
        this.animationMoveId=0
    }
    animationMoveId=0
    requestAnimationMove=()=>{
        if(this.animationMoveId){
            return
        }
        this.animationMoveId=requestAnimationFrame(this.handleAnimationMove)
    }
    getSelectObject(){
        return this.selectObjects
    }
    getHoverObjects(){
        return this.hoverObjects
    }
    _pointerdownslowTimeId=0
    handlePointer = (e: PointerEvent) => {
        if(!this.enabled){
            return
        }
        const type = e.type;
        this.type = type;
        this.pointerEvent=e;
        if (type === 'pointerdown') {
            e.preventDefault()
            this.pointerState=POINTER_STATE.DOWN
            this.getMouse(e, this.pointDown)
            this.getCoord(this.pointDown, this.coord)
            this.pointLast.copy(this.pointDown)
        
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值