leaflet-rotate 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -5
- package/build/test.js +18 -0
- package/dist/leaflet-rotate-src.js +1081 -303
- package/dist/leaflet-rotate-src.js.map +3 -3
- package/dist/leaflet-rotate.js +1 -1
- package/dist/leaflet-rotate.js.map +3 -3
- package/lib/debug.js +84 -66
- package/lib/debug.min.js +1 -1
- package/misc/leaflet-heat-rotate.js +14 -7
- package/misc/loremIpsum.js +1 -1
- package/misc/places.js +1 -1
- package/misc/route.js +1 -1
- package/package.json +5 -5
- package/src/control/Rotate.js +98 -29
- package/src/dom/DomUtil.js +42 -47
- package/src/dom/Draggable.js +6 -1
- package/src/geo/LatLngBounds.js +5 -0
- package/src/geometry/Bounds.js +5 -0
- package/src/geometry/Point.js +33 -10
- package/src/index.js +3 -1
- package/src/layer/DivOverlay.js +43 -12
- package/src/layer/Popup.js +29 -16
- package/src/layer/Tooltip.js +21 -5
- package/src/layer/marker/Icon.js +20 -8
- package/src/layer/marker/Marker.js +103 -24
- package/src/layer/tile/GridLayer.js +22 -6
- package/src/layer/vector/Renderer.js +102 -11
- package/src/map/Map.js +408 -121
- package/src/map/handler/CompassBearing.js +33 -7
- package/src/map/handler/ContainerMutation.js +20 -3
- package/src/map/handler/ShiftKeyRotate.js +156 -16
- package/src/map/handler/TouchGestures.js +91 -29
- package/src/map/handler/TouchRotate.js +20 -3
- package/src/map/handler/TouchZoom.js +25 -5
- package/test/features/example.feature +5 -0
- package/test/features/rotation.feature +30 -0
- package/test/index.html +60 -22
- package/test/steps/bearing.steps.js +345 -3
- package/test/steps/example.steps.js +72 -11
- package/test/steps/gestures.steps.js +4 -4
- package/test/steps/overlays.steps.js +32 -32
- package/test/support/hooks.js +38 -2
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Sets map bearing from device orientation when the browser exposes compass data.
|
|
3
3
|
*
|
|
4
4
|
* @typedef L.Map.CompassBearing
|
|
5
|
+
* @property {L.Map.CompassBearing} compassBearing Map handler, enabled to follow device heading.
|
|
6
|
+
*
|
|
7
|
+
* @since leaflet-rotate (v0.1.0)
|
|
5
8
|
*/
|
|
6
9
|
|
|
7
10
|
L.Map.CompassBearing = L.Handler.extend({
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
/**
|
|
13
|
+
* @param {L.Map} map Map whose bearing follows device orientation.
|
|
14
|
+
*
|
|
15
|
+
* @since leaflet-rotate (v0.1.0)
|
|
16
|
+
*/
|
|
17
|
+
initialize(map) {
|
|
10
18
|
this._map = map;
|
|
11
19
|
/** @see https://caniuse.com/?search=DeviceOrientation */
|
|
12
20
|
if ('ondeviceorientationabsolute' in window) {
|
|
@@ -17,7 +25,12 @@ L.Map.CompassBearing = L.Handler.extend({
|
|
|
17
25
|
this._throttled = L.Util.throttle(this._onDeviceOrientation, 100, this);
|
|
18
26
|
},
|
|
19
27
|
|
|
20
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Subscribe to the supported device-orientation event, if available.
|
|
30
|
+
*
|
|
31
|
+
* @since leaflet-rotate (v0.1.0)
|
|
32
|
+
*/
|
|
33
|
+
addHooks() {
|
|
21
34
|
if (this._map._rotate && this.__deviceOrientationEvent) {
|
|
22
35
|
L.DomEvent.on(window, this.__deviceOrientationEvent, this._throttled, this);
|
|
23
36
|
} else {
|
|
@@ -27,13 +40,20 @@ L.Map.CompassBearing = L.Handler.extend({
|
|
|
27
40
|
}
|
|
28
41
|
},
|
|
29
42
|
|
|
30
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Unsubscribe from device orientation when the handler is disabled.
|
|
45
|
+
*
|
|
46
|
+
* @since leaflet-rotate (v0.1.0)
|
|
47
|
+
*/
|
|
48
|
+
removeHooks() {
|
|
31
49
|
if (this._map._rotate && this.__deviceOrientationEvent) {
|
|
32
50
|
L.DomEvent.off(window, this.__deviceOrientationEvent, this._throttled, this);
|
|
33
51
|
}
|
|
34
52
|
},
|
|
35
53
|
|
|
36
54
|
/**
|
|
55
|
+
* Apply a device orientation reading to the map bearing.
|
|
56
|
+
*
|
|
37
57
|
* `DeviceOrientationEvent.absolute` - Indicates whether the device is providing absolute
|
|
38
58
|
* orientation values (relatives to Magnetic North) or
|
|
39
59
|
* using some arbitrary frame determined by the device.
|
|
@@ -50,10 +70,14 @@ L.Map.CompassBearing = L.Handler.extend({
|
|
|
50
70
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/absolute
|
|
51
71
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/alpha
|
|
52
72
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/orientation
|
|
73
|
+
*
|
|
74
|
+
* @param {DeviceOrientationEvent} e Orientation reading from the browser.
|
|
75
|
+
*
|
|
76
|
+
* @since leaflet-rotate (v0.1.0)
|
|
53
77
|
*/
|
|
54
|
-
_onDeviceOrientation
|
|
55
|
-
|
|
56
|
-
|
|
78
|
+
_onDeviceOrientation(e) {
|
|
79
|
+
let angle = e.webkitCompassHeading != null ? e.webkitCompassHeading : e.alpha;
|
|
80
|
+
let deviceOrientation = 0;
|
|
57
81
|
|
|
58
82
|
if (!Number.isFinite(angle)) { return; }
|
|
59
83
|
|
|
@@ -76,5 +100,7 @@ L.Map.CompassBearing = L.Handler.extend({
|
|
|
76
100
|
* Add Compass bearing handler to L.Map (disabled unless `window.DeviceOrientationEvent` is set).
|
|
77
101
|
*
|
|
78
102
|
* @property {L.Map.CompassBearing} compassBearing
|
|
103
|
+
*
|
|
104
|
+
* @since leaflet-rotate (v0.1.0)
|
|
79
105
|
*/
|
|
80
106
|
L.Map.addInitHook('addHandler', 'compassBearing', L.Map.CompassBearing);
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Calls `invalidateSize()` when the map container's inline style changes.
|
|
3
3
|
*
|
|
4
4
|
* @typedef L.Map.ContainerMutation
|
|
5
|
+
* @property {boolean} [trackContainerMutation=false] Observe inline style changes on the map container.
|
|
6
|
+
*
|
|
7
|
+
* @since leaflet-rotate (v0.1.0)
|
|
5
8
|
*/
|
|
6
9
|
|
|
7
10
|
/**
|
|
@@ -19,6 +22,8 @@ L.Map.mergeOptions({
|
|
|
19
22
|
*
|
|
20
23
|
* @type {Boolean}
|
|
21
24
|
*
|
|
25
|
+
* @since leaflet-rotate (v0.1.0)
|
|
26
|
+
*
|
|
22
27
|
* @see https://developer.mozilla.org/docs/Web/API/MutationObserver
|
|
23
28
|
*/
|
|
24
29
|
trackContainerMutation: false
|
|
@@ -27,7 +32,12 @@ L.Map.mergeOptions({
|
|
|
27
32
|
|
|
28
33
|
L.Map.ContainerMutation = L.Handler.extend({
|
|
29
34
|
|
|
30
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Start observing the map container's style attribute.
|
|
37
|
+
*
|
|
38
|
+
* @since leaflet-rotate (v0.1.0)
|
|
39
|
+
*/
|
|
40
|
+
addHooks() {
|
|
31
41
|
// if (!L.Browser.mutation) { return; }
|
|
32
42
|
if (!this._observer) {
|
|
33
43
|
this._observer = new MutationObserver(L.Util.bind(this._map.invalidateSize, this._map));
|
|
@@ -41,7 +51,12 @@ L.Map.ContainerMutation = L.Handler.extend({
|
|
|
41
51
|
});
|
|
42
52
|
},
|
|
43
53
|
|
|
44
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Stop observing container mutations.
|
|
56
|
+
*
|
|
57
|
+
* @since leaflet-rotate (v0.1.0)
|
|
58
|
+
*/
|
|
59
|
+
removeHooks() {
|
|
45
60
|
// if (!L.Browser.mutation) { return; }
|
|
46
61
|
this._observer.disconnect();
|
|
47
62
|
},
|
|
@@ -52,5 +67,7 @@ L.Map.ContainerMutation = L.Handler.extend({
|
|
|
52
67
|
* Add Container mutation handler to L.Map (disabled unless `trackContainerMutation` is set).
|
|
53
68
|
*
|
|
54
69
|
* @property {L.Map.ContainerMutation} trackContainerMutation
|
|
70
|
+
*
|
|
71
|
+
* @since leaflet-rotate (v0.1.0)
|
|
55
72
|
*/
|
|
56
73
|
L.Map.addInitHook('addHandler', 'trackContainerMutation', L.Map.ContainerMutation);
|
|
@@ -1,55 +1,195 @@
|
|
|
1
1
|
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Handles Shift+wheel bearing changes and Ctrl+drag bearing/pitch changes.
|
|
4
4
|
*
|
|
5
5
|
* @typedef L.Map.ShiftKeyRotate
|
|
6
|
+
* @property {boolean} [shiftKeyRotate=true] Enable Shift+wheel bearing rotation.
|
|
7
|
+
*
|
|
8
|
+
* @since leaflet-rotate (v0.1.0)
|
|
6
9
|
*/
|
|
7
10
|
|
|
8
11
|
L.Map.mergeOptions({
|
|
9
12
|
|
|
10
13
|
/**
|
|
11
|
-
* Whether the map
|
|
14
|
+
* Whether Shift+wheel changes the map bearing.
|
|
15
|
+
*
|
|
12
16
|
* @type {Boolean}
|
|
17
|
+
*
|
|
18
|
+
* @since leaflet-rotate (v0.1.0)
|
|
13
19
|
*/
|
|
14
20
|
shiftKeyRotate: true,
|
|
15
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Whether Ctrl+drag changes bearing and pitch.
|
|
24
|
+
*
|
|
25
|
+
* @type {Boolean}
|
|
26
|
+
*
|
|
27
|
+
* @since leaflet-rotate (0.5.0)
|
|
28
|
+
*/
|
|
29
|
+
pitchDrag: true,
|
|
30
|
+
|
|
16
31
|
});
|
|
17
32
|
|
|
18
33
|
L.Map.ShiftKeyRotate = L.Handler.extend({
|
|
19
34
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
35
|
+
/**
|
|
36
|
+
* @param {L.Map} map Map controlled by this handler.
|
|
37
|
+
*
|
|
38
|
+
* @since leaflet-rotate (v0.1.0)
|
|
39
|
+
*/
|
|
40
|
+
initialize(map) {
|
|
41
|
+
this._map = map;
|
|
42
|
+
this._onMouseDownBound = this._onMouseDown.bind(this);
|
|
43
|
+
this._onMouseMoveBound = this._onMouseMove.bind(this);
|
|
44
|
+
this._onMouseUpBound = this._onMouseUp.bind(this);
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Attach enabled wheel and pointer/mouse gesture listeners.
|
|
49
|
+
*
|
|
50
|
+
* @since leaflet-rotate (v0.1.0)
|
|
51
|
+
*/
|
|
52
|
+
addHooks() {
|
|
53
|
+
if (this._map.options.shiftKeyRotate) {
|
|
54
|
+
L.DomEvent.on(this._map._container, "wheel", this._handleShiftScroll, this);
|
|
55
|
+
}
|
|
56
|
+
if (this._map.options.pitchDrag) {
|
|
57
|
+
this.addPitchHooks();
|
|
58
|
+
}
|
|
24
59
|
},
|
|
25
60
|
|
|
26
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Detach gesture listeners and cancel any active drag.
|
|
63
|
+
*
|
|
64
|
+
* @since leaflet-rotate (v0.1.0)
|
|
65
|
+
*/
|
|
66
|
+
removeHooks() {
|
|
27
67
|
L.DomEvent.off(this._map._container, "wheel", this._handleShiftScroll, this);
|
|
28
|
-
this.
|
|
68
|
+
this.removePitchHooks();
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Listen for the start of Ctrl+drag pitch gestures.
|
|
73
|
+
*
|
|
74
|
+
* @since leaflet-rotate (0.5.0)
|
|
75
|
+
*/
|
|
76
|
+
addPitchHooks() {
|
|
77
|
+
if (this._pitchHooksAdded) { return; }
|
|
78
|
+
this._pitchEvents = L.version.startsWith('2.')
|
|
79
|
+
? ['pointerdown', 'pointermove', 'pointerup pointercancel']
|
|
80
|
+
: ['mousedown', 'mousemove', 'mouseup'];
|
|
81
|
+
this._map._container.addEventListener(this._pitchEvents[0], this._onMouseDownBound, true);
|
|
82
|
+
this._pitchHooksAdded = true;
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Stop listening for pitch gestures and remove active document listeners.
|
|
87
|
+
*
|
|
88
|
+
* @since leaflet-rotate (0.5.0)
|
|
89
|
+
*/
|
|
90
|
+
removePitchHooks() {
|
|
91
|
+
if (!this._pitchHooksAdded) { return; }
|
|
92
|
+
this._map._container.removeEventListener(this._pitchEvents[0], this._onMouseDownBound, true);
|
|
93
|
+
this._onMouseUp();
|
|
94
|
+
delete this._pitchEvents;
|
|
95
|
+
this._pitchHooksAdded = false;
|
|
29
96
|
},
|
|
30
97
|
|
|
31
|
-
|
|
98
|
+
/**
|
|
99
|
+
* Rotate the map in fixed increments while Shift is held during wheel input.
|
|
100
|
+
*
|
|
101
|
+
* @param {WheelEvent} e Wheel event from the map container.
|
|
102
|
+
*
|
|
103
|
+
* @since leaflet-rotate (v0.1.0)
|
|
104
|
+
*/
|
|
105
|
+
_handleShiftScroll(e) {
|
|
32
106
|
if (e.shiftKey) {
|
|
33
107
|
e.preventDefault();
|
|
34
108
|
this._map.scrollWheelZoom.disable();
|
|
35
|
-
this._map.setBearing(
|
|
109
|
+
this._map.setBearing(this._map.getBearing() + Math.sign(e.deltaY) * 5);
|
|
36
110
|
} else {
|
|
37
111
|
this._map.scrollWheelZoom.enable();
|
|
38
112
|
}
|
|
39
113
|
},
|
|
40
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Start Ctrl+drag and capture subsequent movement outside the map.
|
|
117
|
+
*
|
|
118
|
+
* @param {MouseEvent|PointerEvent} e Gesture start event.
|
|
119
|
+
*
|
|
120
|
+
* @since leaflet-rotate (0.5.0)
|
|
121
|
+
*/
|
|
122
|
+
_onMouseDown(e) {
|
|
123
|
+
if (!this._map._rotate || !this._map.options.pitchDrag || !e.ctrlKey || e.button !== 0) { return; }
|
|
124
|
+
|
|
125
|
+
const scrollWheelZoom = this._map.scrollWheelZoom;
|
|
126
|
+
if (scrollWheelZoom && scrollWheelZoom._timer) {
|
|
127
|
+
clearTimeout(scrollWheelZoom._timer);
|
|
128
|
+
scrollWheelZoom._timer = null;
|
|
129
|
+
scrollWheelZoom._delta = 0;
|
|
130
|
+
scrollWheelZoom._startTime = null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
e.preventDefault();
|
|
134
|
+
e.stopPropagation();
|
|
135
|
+
e.stopImmediatePropagation();
|
|
136
|
+
this._lastX = e.clientX;
|
|
137
|
+
this._lastY = e.clientY;
|
|
138
|
+
document.addEventListener(this._pitchEvents[1], this._onMouseMoveBound);
|
|
139
|
+
for (const event of this._pitchEvents[2].split(' ')) {
|
|
140
|
+
document.addEventListener(event, this._onMouseUpBound);
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Apply pointer movement to pitch (vertical) and bearing (horizontal).
|
|
146
|
+
*
|
|
147
|
+
* @param {MouseEvent|PointerEvent} e Gesture movement event.
|
|
148
|
+
*
|
|
149
|
+
* @since leaflet-rotate (0.5.0)
|
|
150
|
+
*/
|
|
151
|
+
_onMouseMove(e) {
|
|
152
|
+
this._map._setPitchAndBearing(
|
|
153
|
+
this._map.getPitch() + (this._lastY - e.clientY) * 0.5,
|
|
154
|
+
this._map.getBearing() + (e.clientX - this._lastX) * 0.8
|
|
155
|
+
);
|
|
156
|
+
this._lastX = e.clientX;
|
|
157
|
+
this._lastY = e.clientY;
|
|
158
|
+
e.preventDefault();
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* End Ctrl+drag and clear document-level listeners.
|
|
163
|
+
*
|
|
164
|
+
* @since leaflet-rotate (0.5.0)
|
|
165
|
+
*/
|
|
166
|
+
_onMouseUp() {
|
|
167
|
+
if (!this._pitchEvents) { return; }
|
|
168
|
+
document.removeEventListener(this._pitchEvents[1], this._onMouseMoveBound);
|
|
169
|
+
for (const event of this._pitchEvents[2].split(' ')) {
|
|
170
|
+
document.removeEventListener(event, this._onMouseUpBound);
|
|
171
|
+
}
|
|
172
|
+
delete this._lastX;
|
|
173
|
+
delete this._lastY;
|
|
174
|
+
},
|
|
175
|
+
|
|
41
176
|
});
|
|
42
177
|
|
|
43
178
|
/**
|
|
44
|
-
*
|
|
179
|
+
* Create the handler when either desktop rotation gesture is enabled.
|
|
45
180
|
*
|
|
46
|
-
* @
|
|
181
|
+
* @since leaflet-rotate (v0.1.0)
|
|
47
182
|
*/
|
|
48
|
-
L.Map.addInitHook('addHandler', 'shiftKeyRotate', L.Map.ShiftKeyRotate);
|
|
49
|
-
|
|
50
|
-
// decrease `scrollWheelZoom` handler priority over `shiftKeyRotate` handler
|
|
51
183
|
L.Map.addInitHook(function() {
|
|
52
|
-
if (this.
|
|
184
|
+
if (this.options.shiftKeyRotate) {
|
|
185
|
+
this.shiftKeyRotate = new L.Map.ShiftKeyRotate(this);
|
|
186
|
+
this.shiftKeyRotate.enable();
|
|
187
|
+
} else if (this.options.pitchDrag) {
|
|
188
|
+
this.shiftKeyRotate = new L.Map.ShiftKeyRotate(this);
|
|
189
|
+
this.shiftKeyRotate.addPitchHooks();
|
|
190
|
+
this.on('unload', this.shiftKeyRotate.removePitchHooks, this.shiftKeyRotate);
|
|
191
|
+
}
|
|
192
|
+
if (this.scrollWheelZoom.enabled() && this.options.shiftKeyRotate) {
|
|
53
193
|
this.scrollWheelZoom.disable();
|
|
54
194
|
this.scrollWheelZoom.enable();
|
|
55
195
|
}
|
|
@@ -1,20 +1,45 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Shared implementation for two-finger pinch zoom and map rotation.
|
|
3
3
|
*
|
|
4
4
|
* @see https://github.com/fnicollet/Leaflet/commit/a77af51a6b10f308d1b9a16552091d1d0aee8834
|
|
5
5
|
* @see https://github.com/Leaflet/Leaflet/blob/v1.9.3/src/map/handler/Map.TouchZoom.js
|
|
6
6
|
*
|
|
7
7
|
* @typedef L.Map.TouchGestures
|
|
8
|
+
* @property {boolean} [bounceAtZoomLimits] Whether pinch zoom may temporarily exceed zoom limits.
|
|
9
|
+
* @property {boolean} zoom Whether pinch zoom is enabled.
|
|
10
|
+
* @property {boolean} rotate Whether touch rotation is enabled.
|
|
11
|
+
*
|
|
12
|
+
* @since leaflet-rotate (v0.1.0)
|
|
8
13
|
*/
|
|
9
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Convert a touch's client coordinates to coordinates relative to the map container.
|
|
17
|
+
*
|
|
18
|
+
* @param {L.Map} map
|
|
19
|
+
* @param {Touch} touch
|
|
20
|
+
*
|
|
21
|
+
* @returns {L.Point}
|
|
22
|
+
*
|
|
23
|
+
* @since leaflet-rotate (v0.1.0)
|
|
24
|
+
*/
|
|
25
|
+
function touchToContainerPoint(map, touch) {
|
|
26
|
+
const container = map.getContainer(),
|
|
27
|
+
rect = container.getBoundingClientRect();
|
|
28
|
+
|
|
29
|
+
return new L.Point(
|
|
30
|
+
touch.clientX - rect.left - container.clientLeft,
|
|
31
|
+
touch.clientY - rect.top - container.clientTop,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
10
35
|
L.Map.mergeOptions({
|
|
11
36
|
|
|
12
37
|
/**
|
|
13
|
-
*
|
|
14
|
-
* zoom beyond min/max zoom and then bounce back
|
|
15
|
-
* when pinch-zooming.
|
|
38
|
+
* Whether pinch zoom may temporarily exceed min/max zoom and bounce back.
|
|
16
39
|
*
|
|
17
40
|
* @type {Boolean}
|
|
41
|
+
*
|
|
42
|
+
* @since leaflet-rotate (v0.1.0)
|
|
18
43
|
*/
|
|
19
44
|
bounceAtZoomLimits: true,
|
|
20
45
|
|
|
@@ -22,27 +47,51 @@ L.Map.mergeOptions({
|
|
|
22
47
|
|
|
23
48
|
L.Map.TouchGestures = L.Handler.extend({
|
|
24
49
|
|
|
25
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Initialize the shared handler from the map's touch zoom/rotation options.
|
|
52
|
+
*
|
|
53
|
+
* @param {L.Map} map Map whose touch gestures are handled.
|
|
54
|
+
*
|
|
55
|
+
* @since leaflet-rotate (v0.1.0)
|
|
56
|
+
*/
|
|
57
|
+
initialize(map) {
|
|
26
58
|
this._map = map;
|
|
27
59
|
this.rotate = !!this._map.options.touchRotate;
|
|
28
60
|
this.zoom = !!this._map.options.touchZoom;
|
|
29
61
|
},
|
|
30
62
|
|
|
31
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Listen for the start of two-finger gestures on the map container.
|
|
65
|
+
*
|
|
66
|
+
* @since leaflet-rotate (v0.1.0)
|
|
67
|
+
*/
|
|
68
|
+
addHooks() {
|
|
32
69
|
L.DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this);
|
|
33
70
|
},
|
|
34
71
|
|
|
35
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Remove the touch-start listener.
|
|
74
|
+
*
|
|
75
|
+
* @since leaflet-rotate (v0.1.0)
|
|
76
|
+
*/
|
|
77
|
+
removeHooks() {
|
|
36
78
|
L.DomEvent.off(this._map._container, 'touchstart', this._onTouchStart, this);
|
|
37
79
|
},
|
|
38
80
|
|
|
39
|
-
|
|
40
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Record initial touch points and start tracking a two-finger gesture.
|
|
83
|
+
*
|
|
84
|
+
* @param {TouchEvent} e Touch-start event.
|
|
85
|
+
*
|
|
86
|
+
* @since leaflet-rotate (v0.1.0)
|
|
87
|
+
*/
|
|
88
|
+
_onTouchStart(e) {
|
|
89
|
+
const map = this._map;
|
|
41
90
|
|
|
42
91
|
if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming || this._rotating) { return; }
|
|
43
92
|
|
|
44
|
-
|
|
45
|
-
p2 = map
|
|
93
|
+
const p1 = touchToContainerPoint(map, e.touches[0]),
|
|
94
|
+
p2 = touchToContainerPoint(map, e.touches[1]),
|
|
46
95
|
vector = p1.subtract(p2);
|
|
47
96
|
|
|
48
97
|
this._centerPoint = map.getSize()._divideBy(2);
|
|
@@ -79,19 +128,26 @@ L.Map.TouchGestures = L.Handler.extend({
|
|
|
79
128
|
L.DomEvent.preventDefault(e);
|
|
80
129
|
},
|
|
81
130
|
|
|
82
|
-
|
|
131
|
+
/**
|
|
132
|
+
* Update bearing and/or zoom while the two touch points move.
|
|
133
|
+
*
|
|
134
|
+
* @param {TouchEvent} e Touch-move event.
|
|
135
|
+
*
|
|
136
|
+
* @since leaflet-rotate (v0.1.0)
|
|
137
|
+
*/
|
|
138
|
+
_onTouchMove(e) {
|
|
83
139
|
if (!e.touches || e.touches.length !== 2 || !(this._zooming || this._rotating)) { return; }
|
|
84
140
|
|
|
85
|
-
|
|
86
|
-
p1 = map
|
|
87
|
-
p2 = map
|
|
141
|
+
const map = this._map,
|
|
142
|
+
p1 = touchToContainerPoint(map, e.touches[0]),
|
|
143
|
+
p2 = touchToContainerPoint(map, e.touches[1]),
|
|
88
144
|
vector = p1.subtract(p2),
|
|
89
|
-
scale = p1.distanceTo(p2) / this._startDist
|
|
90
|
-
|
|
145
|
+
scale = p1.distanceTo(p2) / this._startDist;
|
|
146
|
+
let delta;
|
|
91
147
|
|
|
92
148
|
if (this._rotating) {
|
|
93
|
-
|
|
94
|
-
|
|
149
|
+
const theta = Math.atan(vector.x / vector.y);
|
|
150
|
+
let bearingDelta = (theta - this._startTheta) * (180 / Math.PI);
|
|
95
151
|
if (vector.y < 0) { bearingDelta += 180; }
|
|
96
152
|
if (bearingDelta) {
|
|
97
153
|
/**
|
|
@@ -123,9 +179,12 @@ L.Map.TouchGestures = L.Handler.extend({
|
|
|
123
179
|
delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
|
|
124
180
|
if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
|
|
125
181
|
|
|
126
|
-
|
|
182
|
+
const mapDelta = map._pitch
|
|
183
|
+
? map.containerPointToLayerPoint(this._centerPoint.add(delta))
|
|
184
|
+
.subtract(map.containerPointToLayerPoint(this._centerPoint))
|
|
185
|
+
: delta.rotate(-map.getBearing() * (Math.PI / 180));
|
|
127
186
|
|
|
128
|
-
this._center = map.unproject(map.project(this._pinchStartLatLng).subtract(
|
|
187
|
+
this._center = map.unproject(map.project(this._pinchStartLatLng).subtract(mapDelta));
|
|
129
188
|
}
|
|
130
189
|
|
|
131
190
|
}
|
|
@@ -135,10 +194,8 @@ L.Map.TouchGestures = L.Handler.extend({
|
|
|
135
194
|
this._moved = true;
|
|
136
195
|
}
|
|
137
196
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
var moveFn = map._move.bind(map, this._center, this._zoom, { pinch: true, round: false }, undefined);
|
|
141
|
-
this._animRequest = L.Util.requestAnimFrame(moveFn, this, true);
|
|
197
|
+
const moveFn = map._move.bind(map, this._center, this._zoom, { pinch: true, round: false }, undefined);
|
|
198
|
+
moveFn();
|
|
142
199
|
|
|
143
200
|
if (this.zoom) {
|
|
144
201
|
if (map.options.zoomAnimation) {
|
|
@@ -151,7 +208,12 @@ L.Map.TouchGestures = L.Handler.extend({
|
|
|
151
208
|
L.DomEvent.preventDefault(e);
|
|
152
209
|
},
|
|
153
210
|
|
|
154
|
-
|
|
211
|
+
/**
|
|
212
|
+
* Finish the gesture, settle the zoom and remove document listeners.
|
|
213
|
+
*
|
|
214
|
+
* @since leaflet-rotate (v0.1.0)
|
|
215
|
+
*/
|
|
216
|
+
_onTouchEnd() {
|
|
155
217
|
if (!this._moved || !(this._zooming || this._rotating)) {
|
|
156
218
|
this._zooming = false;
|
|
157
219
|
return;
|
|
@@ -159,8 +221,6 @@ L.Map.TouchGestures = L.Handler.extend({
|
|
|
159
221
|
|
|
160
222
|
this._zooming = false;
|
|
161
223
|
this._rotating = false;
|
|
162
|
-
L.Util.cancelAnimFrame(this._animRequest);
|
|
163
|
-
|
|
164
224
|
L.DomEvent
|
|
165
225
|
.off(document, 'touchmove', this._onTouchMove, this)
|
|
166
226
|
.off(document, 'touchend touchcancel', this._onTouchEnd, this);
|
|
@@ -178,8 +238,10 @@ L.Map.TouchGestures = L.Handler.extend({
|
|
|
178
238
|
});
|
|
179
239
|
|
|
180
240
|
/**
|
|
181
|
-
* Add
|
|
241
|
+
* Add the shared touch gesture handler to the map.
|
|
182
242
|
*
|
|
183
243
|
* @property {L.Map.TouchGestures} touchGestures
|
|
244
|
+
*
|
|
245
|
+
* @since leaflet-rotate (v0.1.0)
|
|
184
246
|
*/
|
|
185
247
|
L.Map.addInitHook('addHandler', 'touchGestures', L.Map.TouchGestures);
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Enables two-finger rotation through the shared touch-gesture handler.
|
|
3
3
|
*
|
|
4
4
|
* @typedef L.Map.TouchRotate
|
|
5
|
+
* @property {boolean} [touchRotate=false] Enable two-finger map rotation.
|
|
6
|
+
*
|
|
7
|
+
* @since leaflet-rotate (v0.1.0)
|
|
5
8
|
*/
|
|
6
9
|
|
|
7
10
|
L.Map.mergeOptions({
|
|
@@ -10,6 +13,8 @@ L.Map.mergeOptions({
|
|
|
10
13
|
* Whether the map can be rotated with a two-finger rotation gesture
|
|
11
14
|
*
|
|
12
15
|
* @type {Boolean}
|
|
16
|
+
*
|
|
17
|
+
* @since leaflet-rotate (v0.1.0)
|
|
13
18
|
*/
|
|
14
19
|
touchRotate: false,
|
|
15
20
|
|
|
@@ -17,12 +22,22 @@ L.Map.mergeOptions({
|
|
|
17
22
|
|
|
18
23
|
L.Map.TouchRotate = L.Handler.extend({
|
|
19
24
|
|
|
20
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Enable rotation handling in the map's shared touch gesture handler.
|
|
27
|
+
*
|
|
28
|
+
* @since leaflet-rotate (v0.1.0)
|
|
29
|
+
*/
|
|
30
|
+
addHooks() {
|
|
21
31
|
this._map.touchGestures.enable();
|
|
22
32
|
this._map.touchGestures.rotate = true;
|
|
23
33
|
},
|
|
24
34
|
|
|
25
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Disable rotation handling without affecting pinch zoom.
|
|
37
|
+
*
|
|
38
|
+
* @since leaflet-rotate (v0.1.0)
|
|
39
|
+
*/
|
|
40
|
+
removeHooks() {
|
|
26
41
|
this._map.touchGestures.rotate = false;
|
|
27
42
|
},
|
|
28
43
|
|
|
@@ -32,5 +47,7 @@ L.Map.TouchRotate = L.Handler.extend({
|
|
|
32
47
|
* Add Touch Rotate handler (disabled unless `touchGestures` is set).
|
|
33
48
|
*
|
|
34
49
|
* @property {L.Map.TouchGestures} touchGestures
|
|
50
|
+
*
|
|
51
|
+
* @since leaflet-rotate (v0.1.0)
|
|
35
52
|
*/
|
|
36
53
|
L.Map.addInitHook('addHandler', 'touchRotate', L.Map.TouchRotate);
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Adds pinch zoom
|
|
2
|
+
* Adds pinch zoom support through the shared touch-gesture handler.
|
|
3
3
|
*
|
|
4
4
|
* @see https://github.com/Leaflet/Leaflet/blob/v1.9.3/src/map/handler/Map.TouchZoom.js
|
|
5
5
|
*
|
|
6
6
|
* @external L.Map.TouchZoom
|
|
7
|
+
* @property {boolean|string} [touchZoom=L.Browser.touch] Enable pinch zoom; `'center'` zooms around the view center.
|
|
8
|
+
* @property {boolean} [bounceAtZoomLimits=false] Allow pinch zoom to exceed zoom limits before settling.
|
|
9
|
+
*
|
|
10
|
+
* @since leaflet-rotate (v0.1.0)
|
|
7
11
|
*/
|
|
8
12
|
|
|
9
13
|
L.Map.mergeOptions({
|
|
@@ -16,6 +20,8 @@ L.Map.mergeOptions({
|
|
|
16
20
|
* for touch-capable web browsers.
|
|
17
21
|
*
|
|
18
22
|
* @type {(Boolean|String)}
|
|
23
|
+
*
|
|
24
|
+
* @since leaflet-rotate (v0.1.0)
|
|
19
25
|
*/
|
|
20
26
|
touchZoom: L.Browser.touch,
|
|
21
27
|
|
|
@@ -27,6 +33,8 @@ L.Map.mergeOptions({
|
|
|
27
33
|
* when pinch-zooming.
|
|
28
34
|
*
|
|
29
35
|
* @type {Boolean}
|
|
36
|
+
*
|
|
37
|
+
* @since leaflet-rotate (v0.1.0)
|
|
30
38
|
*/
|
|
31
39
|
bounceAtZoomLimits: false,
|
|
32
40
|
|
|
@@ -34,14 +42,24 @@ L.Map.mergeOptions({
|
|
|
34
42
|
|
|
35
43
|
L.Map.TouchZoom = L.Handler.extend({
|
|
36
44
|
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Enable pinch zoom handling and mark the shared touch handler as active.
|
|
47
|
+
*
|
|
48
|
+
* @since leaflet-rotate (v0.1.0)
|
|
49
|
+
*/
|
|
50
|
+
addHooks() {
|
|
51
|
+
this._map._container.classList.add('leaflet-touch-zoom');
|
|
39
52
|
this._map.touchGestures.enable();
|
|
40
53
|
this._map.touchGestures.zoom = true;
|
|
41
54
|
},
|
|
42
55
|
|
|
43
|
-
|
|
44
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Disable pinch zoom handling while preserving touch rotation state.
|
|
58
|
+
*
|
|
59
|
+
* @since leaflet-rotate (v0.1.0)
|
|
60
|
+
*/
|
|
61
|
+
removeHooks() {
|
|
62
|
+
this._map._container.classList.remove('leaflet-touch-zoom');
|
|
45
63
|
this._map.touchGestures.zoom = false;
|
|
46
64
|
},
|
|
47
65
|
|
|
@@ -51,5 +69,7 @@ L.Map.TouchZoom = L.Handler.extend({
|
|
|
51
69
|
* Add Touch Zoom handler (disabled unless `L.Browser.touch` is set).
|
|
52
70
|
*
|
|
53
71
|
* @property {L.Map.TouchGestures} touchGestures
|
|
72
|
+
*
|
|
73
|
+
* @since leaflet-rotate (v0.1.0)
|
|
54
74
|
*/
|
|
55
75
|
L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom);
|
|
@@ -27,6 +27,11 @@ Feature: Rotating a Leaflet map
|
|
|
27
27
|
When I apply compass heading readings
|
|
28
28
|
Then valid compass readings update bearing and invalid readings are ignored
|
|
29
29
|
|
|
30
|
+
@issue-33 @issue-41
|
|
31
|
+
Scenario: Shift-wheel rotation can be disabled without disabling wheel zoom
|
|
32
|
+
When I use shift-wheel rotation with the handler disabled
|
|
33
|
+
Then shift-wheel input zooms without changing the map bearing
|
|
34
|
+
|
|
30
35
|
@debugger
|
|
31
36
|
Scenario: Debugger rotation presets and keyboard slider stay synchronized
|
|
32
37
|
When I use the debugger rotation controls
|