Skip to content

Commit 1518c7f

Browse files
feat(core): doubleClickDragZoom gesture (#10327)
1 parent 1075042 commit 1518c7f

7 files changed

Lines changed: 367 additions & 18 deletions

File tree

docs/api-reference/core/controller.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ The base Controller class supports the following options:
1515
* `dragPan` (boolean) - enable panning with pointer drag. Default `true`
1616
* `dragRotate` (boolean) - enable rotating with pointer drag. Default `true`
1717
* `doubleClickZoom` (boolean) - enable zooming with double click. Default `true`
18-
* `touchZoom` (boolean) - enable zooming with multi-touch. Default `true`
18+
* `doubleClickDragZoom` (boolean) - enable zooming by double clicking/tapping and dragging. Default `true`
19+
* `touchZoom` (boolean) - enable zooming with multi-touch pinch. Default `true`
1920
* `touchRotate` (boolean) - enable rotating with multi-touch. Use two-finger rotating gesture for horizontal and three-finger swiping gesture for vertical rotation. Default `false`
2021
* `keyboard` (boolean | object) - enable interaction with keyboard. Default `true`. If an object is supplied, it may contain the following fields to customize the keyboard behavior:
2122
* `zoomSpeed` (number) - speed of zoom using +/- keys. Default `2`.
@@ -26,6 +27,8 @@ The base Controller class supports the following options:
2627
* `inertia` (boolean | number) - Enable inertia after panning/pinching. If a number is provided, indicates the duration of time over which the velocity reduces to zero, in milliseconds. Default `false`.
2728
* `maxBounds` (`[min: number[], max: number[]]`) - constrain camera to the specified bounding box. Different type of views may handle this constraint differently.
2829

30+
> **Mobile users:** See [Optimization for Mobile](../../developer-guide/tips-and-tricks.md#optimization-for-mobile) for CSS and browser event guards that help prevent native selection, tap highlight, and touch callout UI during repeated touch gestures.
31+
2932
## Methods
3033

3134
> A controller is not meant to be instantiated by the application. The following methods are documented for creating custom controllers that extend the base Controller class.
@@ -116,6 +119,7 @@ Note that the following events are always toggled on/off by user options:
116119
* `touchZoom` - `['pinch']`
117120
* `touchRotate` - `['pinch', 'multipan']`
118121
* `doubleClickZoom` - `['dblclick']`
122+
* `doubleClickDragZoom` - `['pointerdown', 'pointermove', 'pointerup', 'pointercancel']`
119123
* `keyboard` - `['keydown']`
120124

121125

docs/developer-guide/tips-and-tricks.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,36 @@ If this is an issue, set the `isolation` CSS prop on the `DeckGL` parent element
5959

6060
## Optimization for Mobile
6161

62+
### Mobile Browser Touch UI
63+
64+
For mobile experiences using a deck.gl [Controller](../api-reference/core/controller.md), use CSS guards on the Deck canvas/root element so repeated touch gestures do not trigger browser selection, tap highlight, or WebKit touch callouts:
65+
66+
```css
67+
#deck-root,
68+
#deck-root canvas {
69+
touch-action: none;
70+
user-select: none;
71+
-webkit-user-select: none;
72+
-webkit-touch-callout: none;
73+
-webkit-tap-highlight-color: transparent;
74+
}
75+
```
76+
77+
Some iOS embeds may still surface native callout UI during long-press or rapid double-tap gestures. In those cases, prevent the browser-native UI events that originate from the canvas without stopping pointer event propagation. The controller still needs deck.gl's pointer events to receive the gesture sequence.
78+
79+
```js
80+
const root = document.getElementById('deck-root');
81+
const preventCanvasBrowserUI = event => {
82+
if (event.target instanceof HTMLCanvasElement) {
83+
event.preventDefault();
84+
}
85+
};
86+
87+
for (const type of ['contextmenu', 'selectstart', 'gesturestart', 'gesturechange', 'gestureend']) {
88+
root.addEventListener(type, preventCanvasBrowserUI, {passive: false});
89+
}
90+
```
91+
6292
### Experimental Memory Usage Controls
6393

6494
The `Deck` class supports the following experimental props to aggressively reduce memory usage on memory-restricted devices:

examples/website/globe/app.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ function getDate(data: DailyFlights[], t: number) {
153153
}
154154

155155
export async function renderToDOM(container: HTMLDivElement) {
156+
// See https://deck.gl/docs/developer-guide/tips-and-tricks#optimization-for-mobile
157+
// for browser UI guards that keep mobile controller gestures focused on the canvas.
158+
addCanvasInteractionGuards(container);
159+
156160
const root = createRoot(container);
157161
root.render(<App />);
158162

@@ -186,3 +190,22 @@ export async function renderToDOM(container: HTMLDivElement) {
186190
root.render(<App data={data} />);
187191
}
188192
}
193+
194+
function addCanvasInteractionGuards(container: HTMLDivElement): void {
195+
const preventCanvasBrowserUI = (event: Event) => {
196+
if (event.target instanceof HTMLCanvasElement) {
197+
event.preventDefault();
198+
}
199+
};
200+
const listenerOptions = {passive: false};
201+
202+
for (const type of [
203+
'contextmenu',
204+
'selectstart',
205+
'gesturestart',
206+
'gesturechange',
207+
'gestureend'
208+
]) {
209+
container.addEventListener(type, preventCanvasBrowserUI, listenerOptions);
210+
}
211+
}

examples/website/globe/index.html

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,41 @@
11
<!doctype html>
22
<html lang="en">
3-
<head>
4-
<meta charset="utf-8">
5-
<title>deck.gl Example</title>
6-
<meta name="viewport" content="width=device-width, initial-scale=1">
7-
<style>
8-
body {margin: 0; font-family: sans-serif; width: 100vw; height: 100vh; overflow: hidden; background: #111;}
9-
</style>
10-
</head>
11-
<body>
12-
<div id="app"></div>
13-
</body>
14-
<script type="module">
15-
import {renderToDOM} from './app.tsx';
16-
renderToDOM(document.getElementById('app'));
17-
</script>
3+
<head>
4+
<meta charset="utf-8" />
5+
<title>deck.gl Example</title>
6+
<meta name="viewport" content="width=device-width, initial-scale=1" />
7+
<style>
8+
html,
9+
body,
10+
#app {
11+
margin: 0;
12+
width: 100%;
13+
height: 100%;
14+
overflow: hidden;
15+
overscroll-behavior: none;
16+
font-family: sans-serif;
17+
background: #111;
18+
}
19+
20+
/*
21+
* See https://deck.gl/docs/developer-guide/tips-and-tricks#optimization-for-mobile
22+
* for CSS guards when using deck.gl controllers in mobile experiences.
23+
*/
24+
#app,
25+
#app canvas {
26+
touch-action: none;
27+
user-select: none;
28+
-webkit-user-select: none;
29+
-webkit-touch-callout: none;
30+
-webkit-tap-highlight-color: transparent;
31+
}
32+
</style>
33+
</head>
34+
<body>
35+
<div id="app"></div>
36+
</body>
37+
<script type="module">
38+
import {renderToDOM} from './app.tsx';
39+
renderToDOM(document.getElementById('app'));
40+
</script>
1841
</html>

0 commit comments

Comments
 (0)