Skip to content
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 );

}

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

}

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