diff --git a/examples/mouse-drag-with-modifier-keys.html b/examples/mouse-drag-with-modifier-keys.html index eb2cabc..de4b990 100644 --- a/examples/mouse-drag-with-modifier-keys.html +++ b/examples/mouse-drag-with-modifier-keys.html @@ -41,53 +41,50 @@ renderer.setSize( width, height ); document.body.appendChild( renderer.domElement ); +const MOUSE_BUTTON = { + LEFT: 1, + RIGHT: 2, + MIDDLE: 4, +} +const ACTION = CameraControls.ACTION + const cameraControls = new CameraControls( camera, renderer.domElement ); -// switch the behavior by the modifier key press -const keyState = { - shiftRight : false, - shiftLeft : false, - controlRight: false, - controlLeft : false, -}; +const originalFunction = cameraControls.mouseEventToAction -const updateConfig = () => { +cameraControls.mouseEventToAction = event => { - if ( keyState.shiftRight || keyState.shiftLeft ) { + if ( event instanceof WheelEvent ) { - cameraControls.mouseButtons.left = CameraControls.ACTION.TRUCK; + return originalFunction(event); - } else if ( keyState.controlRight || keyState.controlLeft ) { + } - cameraControls.mouseButtons.left = CameraControls.ACTION.DOLLY; + if ( event instanceof MouseEvent ) { - } else { + if ( ( event.buttons & MOUSE_BUTTON.LEFT ) === MOUSE_BUTTON.LEFT ) { - cameraControls.mouseButtons.left = CameraControls.ACTION.ROTATE; + if (event.shiftKey) { - } + return ACTION.TRUCK; -} + } + + if (event.ctrlKey) { -document.addEventListener( 'keydown', ( event ) => { + return ACTION.DOLLY; - if ( event.code === 'ShiftRight' ) keyState.shiftRight = true; - if ( event.code === 'ShiftLeft' ) keyState.shiftLeft = true; - if ( event.code === 'ControlRight' ) keyState.controlRight = true; - if ( event.code === 'ControlLeft' ) keyState.controlLeft = true; - updateConfig(); + } -} ); + return ACTION.ROTATE; -document.addEventListener( 'keyup', ( event ) => { + } - if ( event.code === 'ShiftRight' ) keyState.shiftRight = false; - if ( event.code === 'ShiftLeft' ) keyState.shiftLeft = false; - if ( event.code === 'ControlRight' ) keyState.controlRight = false; - if ( event.code === 'ControlLeft' ) keyState.controlLeft = false; - updateConfig(); + } -} ); + return originalFunction(event); + +}; const mesh = new THREE.Mesh( new THREE.BoxGeometry( 1, 1, 1 ), diff --git a/src/CameraControls.ts b/src/CameraControls.ts index 990e868..1fb4c0e 100644 --- a/src/CameraControls.ts +++ b/src/CameraControls.ts @@ -314,23 +314,20 @@ export class CameraControls extends EventDispatcher { // button configs /** * User's mouse input config. + * @param MouseEvent or undefined. Can be a click event, wheel event, or contextmenu event. + * @returns CameraControls.ACTION (ROTATE, TRUCK, OFFSET, DOLLY, ZOOM, NONE) * - * | button to assign | behavior | - * | --------------------- | -------- | - * | `mouseButtons.left` | `CameraControls.ACTION.ROTATE`* \| `CameraControls.ACTION.TRUCK` \| `CameraControls.ACTION.OFFSET` \| `CameraControls.ACTION.DOLLY` \| `CameraControls.ACTION.ZOOM` \| `CameraControls.ACTION.NONE` | - * | `mouseButtons.right` | `CameraControls.ACTION.ROTATE` \| `CameraControls.ACTION.TRUCK`* \| `CameraControls.ACTION.OFFSET` \| `CameraControls.ACTION.DOLLY` \| `CameraControls.ACTION.ZOOM` \| `CameraControls.ACTION.NONE` | - * | `mouseButtons.wheel` ¹ | `CameraControls.ACTION.ROTATE` \| `CameraControls.ACTION.TRUCK` \| `CameraControls.ACTION.OFFSET` \| `CameraControls.ACTION.DOLLY` \| `CameraControls.ACTION.ZOOM` \| `CameraControls.ACTION.NONE` | - * | `mouseButtons.middle` ² | `CameraControls.ACTION.ROTATE` \| `CameraControls.ACTION.TRUCK` \| `CameraControls.ACTION.OFFSET` \| `CameraControls.ACTION.DOLLY`* \| `CameraControls.ACTION.ZOOM` \| `CameraControls.ACTION.NONE` | - * - * 1. Mouse wheel event for scroll "up/down" on mac "up/down/left/right" - * 2. Mouse click on wheel event "button" - * - \* is the default. - * - The default of `mouseButtons.wheel` is: + * - The default of `WheelEvent` is: * - `DOLLY` for Perspective camera. * - `ZOOM` for Orthographic camera, and can't set `DOLLY`. - * @category Properties + * - The default for other `MouseEvent`s is: + * - `ROTATE` if left button is pressed, otherwise + * - `ZOOM` if middle button is pressed, otherwise + * - `TRUCK` if right button is pressed + * - The default is `ROTATE` if event is undefined + * @category Methods */ - mouseButtons: MouseButtons; + mouseEventToAction: ( e?: Event ) => ACTION; /** * User's touch input config. @@ -518,14 +515,40 @@ export class CameraControls extends EventDispatcher { this._dollyControlCoord = new THREE.Vector2(); // configs - this.mouseButtons = { - left: ACTION.ROTATE, - middle: ACTION.DOLLY, - right: ACTION.TRUCK, - wheel: - isPerspectiveCamera( this._camera ) ? ACTION.DOLLY : - isOrthographicCamera( this._camera ) ? ACTION.ZOOM : - ACTION.NONE, + this.mouseEventToAction = ( event: Event | undefined ) => { + + if ( event instanceof WheelEvent ) { + + return isPerspectiveCamera( this._camera ) ? ACTION.DOLLY : + isOrthographicCamera( this._camera ) ? ACTION.ZOOM : + ACTION.NONE; + + } + + if ( event instanceof MouseEvent ) { + + if ( ( event.buttons & MOUSE_BUTTON.LEFT ) === MOUSE_BUTTON.LEFT ) { + + return ACTION.ROTATE; + + } + + if ( ( event.buttons & MOUSE_BUTTON.MIDDLE ) === MOUSE_BUTTON.MIDDLE ) { + + return ACTION.DOLLY; + + } + + if ( ( event.buttons & MOUSE_BUTTON.RIGHT ) === MOUSE_BUTTON.RIGHT ) { + + return ACTION.TRUCK; + + } + + } + + return ACTION.ROTATE; + }; this.touches = { @@ -714,22 +737,9 @@ export class CameraControls extends EventDispatcher { if ( ( ! this._isDragging && this._lockedPointer ) || - this._isDragging && ( event.buttons & MOUSE_BUTTON.LEFT ) === MOUSE_BUTTON.LEFT - ) { - - this._state = this._state | this.mouseButtons.left; - - } - - if ( this._isDragging && ( event.buttons & MOUSE_BUTTON.MIDDLE ) === MOUSE_BUTTON.MIDDLE ) { + this._isDragging && ( event.buttons & MOUSE_BUTTON.ALL ) > 0 ) { - this._state = this._state | this.mouseButtons.middle; - - } - - if ( this._isDragging && ( event.buttons & MOUSE_BUTTON.RIGHT ) === MOUSE_BUTTON.RIGHT ) { - - this._state = this._state | this.mouseButtons.right; + this._state = this._state | this.mouseEventToAction( event ); } @@ -754,22 +764,10 @@ export class CameraControls extends EventDispatcher { if ( this._lockedPointer || - ( event.buttons & MOUSE_BUTTON.LEFT ) === MOUSE_BUTTON.LEFT + ( event.buttons & MOUSE_BUTTON.ALL ) > 0 ) { - this._state = this._state | this.mouseButtons.left; - - } - - if ( ( event.buttons & MOUSE_BUTTON.MIDDLE ) === MOUSE_BUTTON.MIDDLE ) { - - this._state = this._state | this.mouseButtons.middle; - - } - - if ( ( event.buttons & MOUSE_BUTTON.RIGHT ) === MOUSE_BUTTON.RIGHT ) { - - this._state = this._state | this.mouseButtons.right; + this._state = this._state | this.mouseEventToAction( event ); } @@ -839,8 +837,10 @@ export class CameraControls extends EventDispatcher { const onMouseWheel = ( event: WheelEvent ): void => { + const action = this.mouseEventToAction( event ); + if ( ! this._domElement ) return; - if ( ! this._enabled || this.mouseButtons.wheel === ACTION.NONE ) return; + if ( ! this._enabled || action === ACTION.NONE ) return; if ( this._interactiveArea.left !== 0 || @@ -867,8 +867,8 @@ export class CameraControls extends EventDispatcher { if ( this.dollyToCursor || - this.mouseButtons.wheel === ACTION.ROTATE || - this.mouseButtons.wheel === ACTION.TRUCK + action === ACTION.ROTATE || + action === ACTION.TRUCK ) { const now = performance.now(); @@ -885,7 +885,7 @@ export class CameraControls extends EventDispatcher { const x = this.dollyToCursor ? ( event.clientX - this._elementRect.x ) / this._elementRect.width * 2 - 1 : 0; const y = this.dollyToCursor ? ( event.clientY - this._elementRect.y ) / this._elementRect.height * - 2 + 1 : 0; - switch ( this.mouseButtons.wheel ) { + switch ( action ) { case ACTION.ROTATE: { @@ -939,7 +939,7 @@ export class CameraControls extends EventDispatcher { // contextmenu event is fired right after pointerdown/mousedown. // remove attached handlers and active pointer, if interrupted by contextmenu. - if ( this.mouseButtons.right === CameraControls.ACTION.NONE ) { + if ( this.mouseEventToAction( event ) === CameraControls.ACTION.NONE ) { const pointerId = event instanceof PointerEvent ? event.pointerId : @@ -996,7 +996,7 @@ export class CameraControls extends EventDispatcher { if ( ! event ) { - if ( this._lockedPointer ) this._state = this._state | this.mouseButtons.left; + if ( this._lockedPointer ) this._state = this._state | this.mouseEventToAction( event ); } else if ( 'pointerType' in event && event.pointerType === 'touch' ) { @@ -1019,25 +1019,11 @@ export class CameraControls extends EventDispatcher { } - } else { - - if ( ! this._lockedPointer && ( event.buttons & MOUSE_BUTTON.LEFT ) === MOUSE_BUTTON.LEFT ) { - - this._state = this._state | this.mouseButtons.left; - - } - - if ( ( event.buttons & MOUSE_BUTTON.MIDDLE ) === MOUSE_BUTTON.MIDDLE ) { - - this._state = this._state | this.mouseButtons.middle; + } else if ( ! this._lockedPointer && ( event.buttons & MOUSE_BUTTON.LEFT ) === MOUSE_BUTTON.LEFT || + ( event.buttons & MOUSE_BUTTON.MIDDLE ) === MOUSE_BUTTON.MIDDLE || + ( event.buttons & MOUSE_BUTTON.RIGHT ) === MOUSE_BUTTON.RIGHT ) { - } - - if ( ( event.buttons & MOUSE_BUTTON.RIGHT ) === MOUSE_BUTTON.RIGHT ) { - - this._state = this._state | this.mouseButtons.right; - - } + this._state = this._state | this.mouseEventToAction( event ); } @@ -1557,6 +1543,48 @@ export class CameraControls extends EventDispatcher { } + /** + * Convert mouse configuration object into computed configuration function which accepts + * an Event parameter. + */ + set mouseButtons( config: MouseButtons ) { + + this.mouseEventToAction = ( event: Event | undefined ) => { + + if ( event instanceof WheelEvent ) { + + return config.wheel; + + } + + if ( event instanceof MouseEvent ) { + + if ( ( event.buttons & MOUSE_BUTTON.LEFT ) === MOUSE_BUTTON.LEFT ) { + + return config.left; + + } + + if ( ( event.buttons & MOUSE_BUTTON.MIDDLE ) === MOUSE_BUTTON.MIDDLE ) { + + return config.middle; + + } + + if ( ( event.buttons & MOUSE_BUTTON.RIGHT ) === MOUSE_BUTTON.RIGHT ) { + + return config.right; + + } + + } + + return ACTION.ROTATE; + + }; + + } + /** * Adds the specified event listener. * Applicable event types (which is `K`) are: diff --git a/src/types.ts b/src/types.ts index aa28b53..1ec821d 100755 --- a/src/types.ts +++ b/src/types.ts @@ -23,6 +23,7 @@ export const MOUSE_BUTTON = { LEFT: 1, RIGHT: 2, MIDDLE: 4, + ALL: 7, } as const; export type MOUSE_BUTTON = typeof MOUSE_BUTTON[ keyof typeof MOUSE_BUTTON ];