Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change mouse button config to a function (e: Event) => ACTION #459

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 27 additions & 30 deletions examples/mouse-drag-with-modifier-keys.html
Original file line number Diff line number Diff line change
Expand Up @@ -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 ),
Expand Down
176 changes: 102 additions & 74 deletions src/CameraControls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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 );
Comment on lines +740 to +742
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to simplify this conditional because of this change.

( event.buttons & MOUSE_BUTTON.ALL ) > 0 looks a little weird, and technically it would exclude event.buttons === 8 || event.buttons === 16, which is not a common use case. I wanted the result of the function to match previous behaviour exactly.


}

Expand All @@ -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 );

}

Expand Down Expand Up @@ -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 ||
Expand All @@ -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();
Expand All @@ -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: {

Expand Down Expand Up @@ -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 :
Expand Down Expand Up @@ -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' ) {

Expand All @@ -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 ) {
Comment on lines +1022 to +1024
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not use MOUSE_BUTTON.ALL here because having ! this._lockedPointer && changes the logic.


}

if ( ( event.buttons & MOUSE_BUTTON.RIGHT ) === MOUSE_BUTTON.RIGHT ) {

this._state = this._state | this.mouseButtons.right;

}
this._state = this._state | this.mouseEventToAction( event );

}

Expand Down Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 ];

Expand Down