Skip to content

Commit f7a0843

Browse files
fix(core): apply updated GlobeView zoom anchor option
1 parent 0e9285b commit f7a0843

6 files changed

Lines changed: 118 additions & 15 deletions

File tree

docs/api-reference/core/controller.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ The base Controller class supports the following options:
2323
* `rotateSpeedX` (number) - speed of rotation using shift + left/right arrow keys, in degrees. Default `15`.
2424
* `rotateSpeedY` (number) - speed of rotation using shift + up/down arrow keys, in degrees. Default `10`.
2525
* `dragMode` (string) - drag behavior without pressing function keys, one of `pan` and `rotate`.
26+
* `zoomAround` (`'center' | 'pointer'`) - zoom anchor mode when supported by the controller. Default depends on the controller.
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

docs/api-reference/core/globe-controller.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Supports all [Controller options](./controller.md#options) with the following de
4343
- `keyboard`: arrow keys to pan, +/- to zoom
4444
- `inertia`: when set to a number (milliseconds), the globe continues spinning after a fling gesture with exponential decay
4545
- `maxBounds` - constrains the viewport to the specified bounding box `[[minLng, minLat], [maxLng, maxLat]]`
46+
- `zoomAround`: default `'center'`. Set to `'pointer'` to keep the longitude/latitude under the cursor or touch anchor fixed during wheel, pinch, and double-click zoom. If the anchor is off the rendered globe, zoom falls back to center anchoring.
4647

4748
## Custom GlobeController
4849

examples/get-started/pure-js/globe/app.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// SPDX-License-Identifier: MIT
33
// Copyright (c) vis.gl contributors
44

5+
/* global document */
6+
57
import {Deck, _GlobeView as GlobeView} from '@deck.gl/core';
68
import {SolidPolygonLayer, GeoJsonLayer, ArcLayer} from '@deck.gl/layers';
79

@@ -17,10 +19,12 @@ const INITIAL_VIEW_STATE = {
1719
zoom: 0
1820
};
1921

20-
new Deck({
22+
let zoomAround = 'center';
23+
24+
const deckgl = new Deck({
2125
views: new GlobeView(),
2226
initialViewState: INITIAL_VIEW_STATE,
23-
controller: true,
27+
controller: {zoomAround},
2428
layers: [
2529
// A GeoJSON polygon that covers the entire earth
2630
// See /docs/api-reference/globe-view.md#remarks
@@ -75,3 +79,16 @@ new Deck({
7579
})
7680
]
7781
});
82+
83+
function setZoomAround(nextZoomAround) {
84+
zoomAround = nextZoomAround;
85+
deckgl.setProps({controller: {zoomAround}});
86+
for (const button of zoomButtons) {
87+
button.setAttribute('aria-pressed', String(button.dataset.zoomAround === zoomAround));
88+
}
89+
}
90+
91+
const zoomButtons = document.querySelectorAll('[data-zoom-around]');
92+
for (const button of zoomButtons) {
93+
button.addEventListener('click', () => setZoomAround(button.dataset.zoomAround));
94+
}
Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,71 @@
11
<!doctype html>
22
<html lang="en">
3-
<head>
4-
<meta charset="utf-8">
5-
<title>deck.gl Example</title>
6-
<style>
7-
body {margin: 0; width: 100vw; height: 100vh; overflow: hidden;}
8-
</style>
9-
</head>
10-
<body>
11-
</body>
12-
<script type="module" src="app.js"></script>
3+
<head>
4+
<meta charset="utf-8" />
5+
<title>deck.gl Example</title>
6+
<style>
7+
body {
8+
margin: 0;
9+
width: 100vw;
10+
height: 100vh;
11+
overflow: hidden;
12+
background: #111;
13+
}
14+
.zoom-control {
15+
position: absolute;
16+
z-index: 1;
17+
top: 24px;
18+
right: 24px;
19+
padding: 12px;
20+
border: 1px solid rgba(255, 255, 255, 0.22);
21+
border-radius: 6px;
22+
background: rgba(8, 14, 24, 0.78);
23+
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.28);
24+
color: #fff;
25+
font-family: Helvetica, Arial, sans-serif;
26+
}
27+
.zoom-control-label {
28+
margin-bottom: 8px;
29+
color: rgba(255, 255, 255, 0.82);
30+
font-size: 12px;
31+
font-weight: 600;
32+
line-height: 1;
33+
}
34+
.zoom-button-group {
35+
display: flex;
36+
}
37+
.zoom-button-group button {
38+
min-width: 70px;
39+
padding: 8px 10px;
40+
border: 1px solid rgba(122, 136, 255, 0.64);
41+
background: rgba(12, 18, 34, 0.72);
42+
color: rgba(205, 213, 255, 0.9);
43+
cursor: pointer;
44+
font-size: 12px;
45+
font-weight: 700;
46+
line-height: 1;
47+
}
48+
.zoom-button-group button:first-child {
49+
border-radius: 4px 0 0 4px;
50+
}
51+
.zoom-button-group button:last-child {
52+
margin-left: -1px;
53+
border-radius: 0 4px 4px 0;
54+
}
55+
.zoom-button-group button[aria-pressed='true'] {
56+
background: #5262df;
57+
color: #fff;
58+
}
59+
</style>
60+
</head>
61+
<body>
62+
<div class="zoom-control">
63+
<div class="zoom-control-label">Zoom anchor</div>
64+
<div class="zoom-button-group" role="group" aria-label="Zoom anchor">
65+
<button type="button" data-zoom-around="center" aria-pressed="true">Center</button>
66+
<button type="button" data-zoom-around="pointer" aria-pressed="false">Pointer</button>
67+
</div>
68+
</div>
69+
</body>
70+
<script type="module" src="app.js"></script>
1371
</html>

modules/core/src/controllers/controller.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,8 @@ export default abstract class Controller<ControllerState extends IViewState<Cont
244244
get controllerState(): ControllerState {
245245
this._controllerState = this._controllerState || new this.ControllerState({
246246
makeViewport: this.makeViewport,
247-
...this.props,
248-
...this.state
247+
...this.state,
248+
...this.props
249249
});
250250
return this._controllerState;
251251
}

test/modules/core/controllers/controllers.spec.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ test('GlobeController', async () => {
4949
});
5050

5151
test('GlobeController supports pointer anchored zoom option', () => {
52-
const makeController = (controller: true | {zoomAround: 'pointer'}) =>
52+
const makeController = (controller: true | {zoomAround: 'center' | 'pointer'}) =>
5353
createTestController({
5454
view: new GlobeView({controller}),
5555
initialViewState: {
@@ -79,6 +79,32 @@ test('GlobeController supports pointer anchored zoom option', () => {
7979
);
8080
});
8181

82+
test('GlobeController applies updated zoomAround option', () => {
83+
const controller = createTestController({
84+
view: new GlobeView({controller: {zoomAround: 'center'}}),
85+
initialViewState: {
86+
longitude: 0,
87+
latitude: 0,
88+
zoom: 1
89+
}
90+
});
91+
92+
const wheelEvent = {
93+
type: 'wheel',
94+
offsetCenter: {x: 75, y: 50},
95+
delta: -10,
96+
srcEvent: {preventDefault() {}},
97+
stopPropagation() {}
98+
};
99+
100+
controller.handleEvent(wheelEvent as any);
101+
expect(controller.props.longitude, 'center zoom preserves longitude').toBeCloseTo(0);
102+
103+
controller.setProps({...controller.props, zoomAround: 'pointer'});
104+
controller.handleEvent(wheelEvent as any);
105+
expect(controller.props.longitude, 'updated pointer zoom adjusts longitude').not.toBeCloseTo(0);
106+
});
107+
82108
test('OrbitController', async () => {
83109
await testController(OrbitView, {
84110
orbitAxis: 'Y',

0 commit comments

Comments
 (0)