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

Upgrade mjolnir.js to v3 #9144

Merged
merged 6 commits into from
Sep 26, 2024
Merged
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
62 changes: 35 additions & 27 deletions docs/api-reference/core/controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,22 +53,9 @@ The constructor takes one argument:

#### `handleEvent(event)` {#handleevent}

Called by the event manager to handle pointer events. This method delegate to the following methods to handle the default events:
Called by the event manager to handle pointer events.

* `_onPanStart(event)`
* `_onPan(event)`
* `_onPanEnd(event)`
* `_onPinchStart(event)`
* `_onPinch(event)`
* `_onPinchEnd(event)`
* `_onTriplePanStart(event)`
* `_onTriplePan(event)`
* `_onTriplePanEnd(event)`
* `_onDoubleTap(event)`
* `_onWheel(event)`
* `_onKeyDown(event)`

See [Event object documentation](https://uber-web.github.io/mjolnir.js/docs/api-reference/event).
See [Event object documentation](https://visgl.github.io/mjolnir.js/docs/api-reference/event).


#### `setProps(props)` {#setprops}
Expand Down Expand Up @@ -98,6 +85,39 @@ If `event` is provided, returns `false` if the event is already handled, and mar
Returns `true` if the user is dragging the view.


## Members

#### `events` (string[])

In its constructor, a controller class can optionally specify a list of event names that it subscribes to with the `events` field.
Supported events are:

* `click`
* `dblclick`
* `pan`
* `pinch`: 2-finger free-form manipulation, used for touch zooming and rotation
* `multipan`: 2-finger vertical panning, used for touch pitching in `MapController`
* `keydown`
* `keyup`
* `pointerdown`
* `pointermove`
* `pointerup`
* `pointerover`
* `pointerout`
* `pointerleave`
* `wheel`
* `contextmenu`

Note that the following events are always toggled on/off by user options:

* `scrollZoom` - `['wheel']`
* `dragPan` and `dragRotate` - `['pan']`
* `touchZoom` - `['pinch']`
* `touchRotate` - `['pinch', 'multipan']`
* `doubleClickZoom` - `['dblclick']`
* `keyboard` - `['keydown']`


## Example: Implementing A Custom Controller

```js
Expand All @@ -119,18 +139,6 @@ class MyController extends Controller{
}
```

In its constructor, a controller class can optionally specify a list of event names that it subscribes to with the `events` field. A full list of supported events can be found [here](https://uber-web.github.io/mjolnir.js/docs/api-reference/event-manager#supported-events-and-gestures).

Note that the following events are always toggled on/off by user options:

* `scrollZoom` - `['wheel']`
* `dragPan` and `dragRotate` - `['pan']`
* `touchZoom` - `['pinch']`
* `touchRotate` - `['pinch', 'tripan']`
* `doubleClickZoom` - `['doubletap']`
* `keyboard` - `['keydown']`


## Source

[modules/core/src/controllers/controller.ts](https://github.com/visgl/deck.gl/blob/master/modules/core/src/controllers/controller.ts)
10 changes: 5 additions & 5 deletions docs/api-reference/core/deck.md
Original file line number Diff line number Diff line change
Expand Up @@ -401,11 +401,11 @@ By default, the deck canvas captures all touch interactions. This prop is useful

Set options for gesture recognition. May contain the following fields:

- `pan` - an object that is [Hammer.Pan](http://hammerjs.github.io/recognizer-pan/) options. This gesture is used for drag events.
- `pinch` - an object that is [Hammer.Pinch](http://hammerjs.github.io/recognizer-pinch/) options This gesture is used for two-finger touch events.
- `tripan` - an object that is [Hammer.Pan](http://hammerjs.github.io/recognizer-pan/) options. This gesture is used for three-finger touch events.
- `tap` - an object that is [Hammer.Tap](http://hammerjs.github.io/recognizer-tap/) options. This gesture is used for the `onClick` callback.
- `doubletap` - an object that is [Hammer.Tap](http://hammerjs.github.io/recognizer-tap/) options. This gesture is used for double click events.
- `pan` - an object that is [Pan](https://visgl.github.io/mjolnir.js/docs/api-reference/pan) options. This gesture is used for `onDrag` events, viewport panning (mouse/touch) and rotating (mouse+ctrl). Default `{threshold: 1}`.
- `pinch` - an object that is [Pinch](https://visgl.github.io/mjolnir.js/docs/api-reference/pinch) options This gesture is used for multi-touch zooming/rotating.
- `multipan` - an object that is [Pan](https://visgl.github.io/mjolnir.js/docs/api-reference/pan) options. This gesture is used for multi-touch pitching. Default `{threshold: 10, direction: InputDirection.Vertical, pointers: 2}`.
- `click` - an object that is [Tap](https://visgl.github.io/mjolnir.js/docs/api-reference/tap) options. This gesture is used for the `onClick` event.
- `dblclick` - an object that is [Tap](https://visgl.github.io/mjolnir.js/docs/api-reference/tap) options. This gesture is used for double-click zooming.

For example, the following setting makes panning less sensitive and clicking easier on mobile:

Expand Down
16 changes: 16 additions & 0 deletions docs/upgrade-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@

## Upgrading to v9.1

### User input handling

- The main gesture recognition library, [mjolnir.js](https://visgl.github.io/mjolnir.js), is upgraded to v3.0. Hammer.js is no longer an (indirect) dependency, due to its lack of maintenance and various issues with SSR and test environments.
- The default gesture to manipulate viewport `pitch` by touch has been changed from three-finger to two-finger dragging vertically. You can revert this behavior by setting the [eventRecognizerOptions](./api-reference/core/deck.md#eventrecognizeroptions) prop of `Deck`:

```ts
eventRecognizerOptions: {
multipan: {pointers: 3}
}
```

- If your app already uses Deck's `eventRecognizerOptions` prop or implement a custom [Controller](./api-reference/core/controller.md), some events have been renamed:
+ `tripan` -> `multipan`
+ `tap` -> `click`
+ `doubletap` -> `dblclick`

### Aggregation layers

Breaking changes:
Expand Down
1 change: 1 addition & 0 deletions examples/vite.config.local.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export default defineConfig(async () => {
alias: {
...aliases,
// Use root dependencies
'mjolnir.js': join(rootDir, './node_modules/mjolnir.js'),
'@luma.gl': join(rootDir, './node_modules/@luma.gl'),
'@math.gl': join(rootDir, './node_modules/@math.gl'),
'@arcgis/core': join(rootDir, './node_modules/@arcgis/core'),
Expand Down
2 changes: 1 addition & 1 deletion modules/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"@probe.gl/stats": "^4.0.9",
"@types/offscreencanvas": "^2019.6.4",
"gl-matrix": "^3.0.0",
"mjolnir.js": "^2.7.0"
"mjolnir.js": "^3.0.0-alpha.3"
},
"gitHead": "13ace64fc2cee08c133afc882fc307253489a4e4"
}
34 changes: 17 additions & 17 deletions modules/core/src/controllers/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ const EVENT_TYPES = {
WHEEL: ['wheel'],
PAN: ['panstart', 'panmove', 'panend'],
PINCH: ['pinchstart', 'pinchmove', 'pinchend'],
TRIPLE_PAN: ['tripanstart', 'tripanmove', 'tripanend'],
DOUBLE_TAP: ['doubletap'],
MULTI_PAN: ['multipanstart', 'multipanmove', 'multipanend'],
DOUBLE_CLICK: ['dblclick'],
KEYBOARD: ['keydown']
} as const;

Expand Down Expand Up @@ -228,14 +228,14 @@ export default abstract class Controller<ControllerState extends IViewState<Cont
return this._onPinch(event);
case 'pinchend':
return this._onPinchEnd(event);
case 'tripanstart':
return eventStartBlocked ? false : this._onTriplePanStart(event);
case 'tripanmove':
return this._onTriplePan(event);
case 'tripanend':
return this._onTriplePanEnd(event);
case 'doubletap':
return this._onDoubleTap(event);
case 'multipanstart':
return eventStartBlocked ? false : this._onMultiPanStart(event);
case 'multipanmove':
return this._onMultiPan(event);
case 'multipanend':
return this._onMultiPanEnd(event);
case 'dblclick':
return this._onDoubleClick(event);
case 'wheel':
return this._onWheel(event);
case 'keydown':
Expand Down Expand Up @@ -333,8 +333,8 @@ export default abstract class Controller<ControllerState extends IViewState<Cont
// We always need the pan events to set the correct isDragging state, even if dragPan & dragRotate are both false
this.toggleEvents(EVENT_TYPES.PAN, isInteractive);
this.toggleEvents(EVENT_TYPES.PINCH, isInteractive && (touchZoom || touchRotate));
this.toggleEvents(EVENT_TYPES.TRIPLE_PAN, isInteractive && touchRotate);
this.toggleEvents(EVENT_TYPES.DOUBLE_TAP, isInteractive && doubleClickZoom);
this.toggleEvents(EVENT_TYPES.MULTI_PAN, isInteractive && touchRotate);
this.toggleEvents(EVENT_TYPES.DOUBLE_CLICK, isInteractive && doubleClickZoom);
this.toggleEvents(EVENT_TYPES.KEYBOARD, isInteractive && keyboard);

// Interaction toggles
Expand Down Expand Up @@ -561,7 +561,7 @@ export default abstract class Controller<ControllerState extends IViewState<Cont
return true;
}

protected _onTriplePanStart(event: MjolnirGestureEvent): boolean {
protected _onMultiPanStart(event: MjolnirGestureEvent): boolean {
const pos = this.getCenter(event);
if (!this.isPointInBounds(pos, event)) {
return false;
Expand All @@ -571,7 +571,7 @@ export default abstract class Controller<ControllerState extends IViewState<Cont
return true;
}

protected _onTriplePan(event: MjolnirGestureEvent): boolean {
protected _onMultiPan(event: MjolnirGestureEvent): boolean {
if (!this.touchRotate) {
return false;
}
Expand All @@ -590,7 +590,7 @@ export default abstract class Controller<ControllerState extends IViewState<Cont
return true;
}

protected _onTriplePanEnd(event: MjolnirGestureEvent): boolean {
protected _onMultiPanEnd(event: MjolnirGestureEvent): boolean {
if (!this.isDragging()) {
return false;
}
Expand Down Expand Up @@ -713,8 +713,8 @@ export default abstract class Controller<ControllerState extends IViewState<Cont
return true;
}

// Default handler for the `doubletap` event.
protected _onDoubleTap(event: MjolnirGestureEvent): boolean {
// Default handler for the `dblclick` event.
protected _onDoubleClick(event: MjolnirGestureEvent): boolean {
if (!this.doubleClickZoom) {
return false;
}
Expand Down
32 changes: 27 additions & 5 deletions modules/core/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@
// "project" and "project64" shader modules. Both places need to be
// updated.
import log from '../utils/log';
import {Pan, InputDirection, Pinch, Tap} from 'mjolnir.js';
import type {
PanRecognizerOptions,
RotateRecognizerOptions,
PinchRecognizerOptions,
TapRecognizerOptions
} from 'mjolnir.js';

/**
* The coordinate system that positions/dimensions are defined in.
Expand Down Expand Up @@ -100,13 +107,28 @@ export const UNIT = {
pixels: 2
} as const;

export const EVENTS = {
click: {handler: 'onClick'},
panstart: {handler: 'onDragStart'},
panmove: {handler: 'onDrag'},
panend: {handler: 'onDragEnd'}
export const EVENT_HANDLERS: {[eventName: string]: string} = {
click: 'onClick',
panstart: 'onDragStart',
panmove: 'onDrag',
panend: 'onDragEnd'
} as const;

export const RECOGNIZERS = {
multipan: [Pan, {threshold: 10, direction: InputDirection.Vertical, pointers: 2}],
pinch: [Pinch, {}, null, ['multipan']],
pan: [Pan, {threshold: 1}, ['pinch'], ['multipan']],
dblclick: [Tap, {event: 'dblclick', taps: 2}],
click: [Tap, {event: 'click'}, null, ['dblclick']]
} as const;
export type RecognizerOptions = {
pinch?: Omit<PinchRecognizerOptions, 'event' | 'enable'>;
multipan?: Omit<PanRecognizerOptions, 'event' | 'enable'>;
pan?: Omit<PanRecognizerOptions, 'event' | 'enable'>;
dblclick?: Omit<TapRecognizerOptions, 'event' | 'enable'>;
click?: Omit<TapRecognizerOptions, 'event' | 'enable'>;
};

/**
* @deprecated Use string constants directly
*/
Expand Down
42 changes: 26 additions & 16 deletions modules/core/src/lib/deck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ import {Stats} from '@probe.gl/stats';
import {EventManager} from 'mjolnir.js';

import assert from '../utils/assert';
import {EVENTS} from './constants';
import {EVENT_HANDLERS, RECOGNIZERS, RecognizerOptions} from './constants';

import type {Effect} from './effect';
import type {FilterContext} from '../passes/layers-pass';
import type Layer from './layer';
import type View from '../views/view';
import type Viewport from '../viewports/viewport';
import type {RecognizerOptions, MjolnirGestureEvent, MjolnirPointerEvent} from 'mjolnir.js';
import type {EventManagerOptions, MjolnirGestureEvent, MjolnirPointerEvent} from 'mjolnir.js';
import type {TypedArrayManagerOptions} from '../utils/typed-array-manager';
import type {ViewStateChangeParameters, InteractionState} from '../controllers/controller';
import type {PickingInfo} from './picking/pick-info';
Expand Down Expand Up @@ -167,11 +167,11 @@ export type DeckProps<ViewsT extends ViewOrViews = null> = {
/** Allow browser default touch actions.
* @default `'none'`
*/
touchAction?: string;
/** Set Hammer.js recognizer options for gesture recognition. See documentation for details. */
eventRecognizerOptions?: {
[type: string]: RecognizerOptions;
};
touchAction?: EventManagerOptions['touchAction'];
/**
* Optional mjolnir.js recognizer options
*/
eventRecognizerOptions?: RecognizerOptions;

/** (Experimental) Render to a custom frame buffer other than to screen. */
_framebuffer?: Framebuffer | null;
Expand Down Expand Up @@ -234,7 +234,7 @@ export type DeckProps<ViewsT extends ViewOrViews = null> = {
drawPickingColors?: boolean;
};

const defaultProps = {
const defaultProps: DeckProps = {
id: '',
width: '100%',
height: '100%',
Expand Down Expand Up @@ -971,15 +971,26 @@ export default class Deck<ViewsT extends ViewOrViews = null> {

this.eventManager = new EventManager(this.props.parent || this.canvas, {
touchAction: this.props.touchAction,
recognizerOptions: this.props.eventRecognizerOptions,
recognizers: Object.keys(RECOGNIZERS).map((eventName: string) => {
// Resolve recognizer settings
const [RecognizerConstructor, defaultOptions, recognizeWith, requestFailure] =
RECOGNIZERS[eventName];
const optionsOverride = this.props.eventRecognizerOptions?.[eventName];
const options = {...defaultOptions, ...optionsOverride, event: eventName};
return {
recognizer: new RecognizerConstructor(options),
recognizeWith,
requestFailure
};
}),
events: {
pointerdown: this._onPointerDown,
pointermove: this._onPointerMove,
pointerleave: this._onPointerMove
}
});
for (const eventType in EVENTS) {
this.eventManager.on(eventType as keyof typeof EVENTS, this._onEvent);
for (const eventType in EVENT_HANDLERS) {
this.eventManager.on(eventType, this._onEvent);
}

this.viewManager = new ViewManager({
Expand Down Expand Up @@ -1133,10 +1144,10 @@ export default class Deck<ViewsT extends ViewOrViews = null> {

/** Internal use only: event handler for click & drag */
_onEvent = (event: MjolnirGestureEvent) => {
const eventOptions = EVENTS[event.type];
const eventHandlerProp = EVENT_HANDLERS[event.type];
const pos = event.offsetCenter;

if (!eventOptions || !pos || !this.layerManager) {
if (!eventHandlerProp || !pos || !this.layerManager) {
return;
}

Expand All @@ -1153,9 +1164,8 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
) as PickingInfo;

const {layer} = info;
const layerHandler =
layer && (layer[eventOptions.handler] || layer.props[eventOptions.handler]);
const rootHandler = this.props[eventOptions.handler];
const layerHandler = layer && (layer[eventHandlerProp] || layer.props[eventHandlerProp]);
const rootHandler = this.props[eventHandlerProp];
let handled = false;

if (layerHandler) {
Expand Down
8 changes: 4 additions & 4 deletions modules/core/src/lib/widget-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type {PickingInfo} from './picking/pick-info';
import type {MjolnirPointerEvent, MjolnirGestureEvent} from 'mjolnir.js';
import type Layer from './layer';

import {EVENTS} from './constants';
import {EVENT_HANDLERS} from './constants';
import {deepEqual} from '../utils/deep-equal';

export interface Widget<PropsT = any> {
Expand Down Expand Up @@ -278,14 +278,14 @@ export class WidgetManager {
}

onEvent(info: PickingInfo, event: MjolnirGestureEvent) {
const eventOptions = EVENTS[event.type];
if (!eventOptions) {
const eventHandlerProp = EVENT_HANDLERS[event.type];
if (!eventHandlerProp) {
return;
}
for (const widget of this.getWidgets()) {
const {viewId} = widget;
if (!viewId || viewId === info.viewport?.id) {
widget[eventOptions.handler]?.(info, event);
widget[eventHandlerProp]?.(info, event);
}
}
}
Expand Down
Loading
Loading