ts-maps 0.3.6 → 0.3.7
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.
|
@@ -1,12 +1,52 @@
|
|
|
1
1
|
import { Handler } from '../../core/Handler';
|
|
2
|
+
import type { LatLng } from '../../geo/LatLng';
|
|
2
3
|
import type { Point } from '../../geometry/Point';
|
|
4
|
+
/**
|
|
5
|
+
* Continuous scroll-wheel zoom.
|
|
6
|
+
*
|
|
7
|
+
* The previous implementation was Leaflet's: accumulate wheel delta, wait
|
|
8
|
+
* 40ms for the gesture to stop, then animate one discrete `setZoom` step. That
|
|
9
|
+
* is three separate problems stacked on each other — the map does not move
|
|
10
|
+
* while you are actually scrolling, it then moves in a jump you did not ask
|
|
11
|
+
* for, and a trackpad's continuous stream of small deltas is chopped into a
|
|
12
|
+
* sequence of those jumps. Next to Google Maps or Apple Maps it reads as
|
|
13
|
+
* broken rather than as slow.
|
|
14
|
+
*
|
|
15
|
+
* What every other slippy map does instead, and what this does:
|
|
16
|
+
*
|
|
17
|
+
* - Wheel events push a TARGET zoom. They never animate anything themselves,
|
|
18
|
+
* so a fast scroll and a slow one differ in how far they go, not in how
|
|
19
|
+
* many animations they queue.
|
|
20
|
+
* - A single rAF loop eases the live zoom toward that target and stops when
|
|
21
|
+
* it arrives. Scrolling again just moves the target; the loop is already
|
|
22
|
+
* running and simply keeps going. There is nothing to cancel and nothing
|
|
23
|
+
* to queue.
|
|
24
|
+
* - The point under the cursor stays under the cursor, recomputed every
|
|
25
|
+
* frame from the anchor the gesture started at.
|
|
26
|
+
*
|
|
27
|
+
* ## Trackpads
|
|
28
|
+
*
|
|
29
|
+
* A pinch on a macOS trackpad arrives as a `wheel` event with `ctrlKey` set —
|
|
30
|
+
* the browser's way of reporting a zoom gesture, and nothing to do with the
|
|
31
|
+
* Control key. Those deltas are much finer than a mouse notch, so applying the
|
|
32
|
+
* mouse's px-per-level to them makes a pinch cover half the zoom range. They
|
|
33
|
+
* get their own scale, and no easing: a pinch is a direct manipulation, and
|
|
34
|
+
* anything that lags behind the fingers feels broken.
|
|
35
|
+
*/
|
|
3
36
|
export declare class ScrollWheelZoomHandler extends Handler {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
37
|
+
_targetZoom: number | null;
|
|
38
|
+
_anchor?: Point;
|
|
39
|
+
_anchorLatLng?: LatLng;
|
|
40
|
+
_frame?: number;
|
|
41
|
+
_lastFrameTime: number;
|
|
42
|
+
_active: boolean;
|
|
8
43
|
addHooks(): void;
|
|
9
44
|
removeHooks(): void;
|
|
10
|
-
_onWheelScroll(e:
|
|
11
|
-
|
|
45
|
+
_onWheelScroll(e: WheelEvent): void;
|
|
46
|
+
_applyZoom(zoom: number): void;
|
|
47
|
+
_startFrames(): void;
|
|
48
|
+
_stopFrames(): void;
|
|
49
|
+
_tick(timestamp: number): void;
|
|
50
|
+
_scheduleSettle(): void;
|
|
51
|
+
_settle(): void;
|
|
12
52
|
}
|
package/dist/index.js
CHANGED
|
@@ -23590,50 +23590,113 @@ init_DomEvent();
|
|
|
23590
23590
|
init_Map();
|
|
23591
23591
|
TsMap.mergeOptions({
|
|
23592
23592
|
scrollWheelZoom: true,
|
|
23593
|
-
|
|
23594
|
-
|
|
23593
|
+
wheelPxPerZoomLevel: 60,
|
|
23594
|
+
wheelSmoothing: 0.13,
|
|
23595
|
+
wheelDebounceTime: 40
|
|
23595
23596
|
});
|
|
23596
23597
|
|
|
23597
23598
|
class ScrollWheelZoomHandler extends Handler {
|
|
23598
|
-
_delta = 0;
|
|
23599
|
-
_startTime = null;
|
|
23600
23599
|
addHooks() {
|
|
23601
23600
|
on(this._map._container, "wheel", this._onWheelScroll, this);
|
|
23602
|
-
this.
|
|
23601
|
+
this._targetZoom = null;
|
|
23602
|
+
this._active = false;
|
|
23603
|
+
this._lastFrameTime = 0;
|
|
23603
23604
|
}
|
|
23604
23605
|
removeHooks() {
|
|
23605
23606
|
off(this._map._container, "wheel", this._onWheelScroll, this);
|
|
23606
|
-
|
|
23607
|
+
this._stopFrames();
|
|
23608
|
+
this._targetZoom = null;
|
|
23609
|
+
this._active = false;
|
|
23607
23610
|
}
|
|
23608
23611
|
_onWheelScroll(e) {
|
|
23612
|
+
const map = this._map;
|
|
23609
23613
|
const delta = getWheelDelta(e);
|
|
23610
|
-
const debounce = this._map.options.wheelDebounceTime;
|
|
23611
|
-
this._delta += delta;
|
|
23612
|
-
this._lastMousePos = this._map.pointerEventToContainerPoint(e);
|
|
23613
|
-
if (!this._startTime)
|
|
23614
|
-
this._startTime = Date.now();
|
|
23615
|
-
const left = Math.max(debounce - (Date.now() - this._startTime), 0);
|
|
23616
|
-
clearTimeout(this._timer);
|
|
23617
|
-
this._timer = setTimeout(this._performZoom.bind(this), left);
|
|
23618
23614
|
stop(e);
|
|
23615
|
+
if (!delta)
|
|
23616
|
+
return;
|
|
23617
|
+
const pinch = e.ctrlKey === true;
|
|
23618
|
+
const pxPerLevel = map.options.wheelPxPerZoomLevel;
|
|
23619
|
+
const step = delta / (pinch ? pxPerLevel * 2 : pxPerLevel);
|
|
23620
|
+
if (!this._active) {
|
|
23621
|
+
this._anchor = map.pointerEventToContainerPoint(e);
|
|
23622
|
+
this._anchorLatLng = map.containerPointToLatLng(this._anchor);
|
|
23623
|
+
this._targetZoom = map.getZoom();
|
|
23624
|
+
this._active = true;
|
|
23625
|
+
map._stop();
|
|
23626
|
+
map._moveStart(true, false);
|
|
23627
|
+
}
|
|
23628
|
+
this._targetZoom = map._limitZoom(this._targetZoom + step);
|
|
23629
|
+
if (pinch) {
|
|
23630
|
+
this._applyZoom(this._targetZoom);
|
|
23631
|
+
this._scheduleSettle();
|
|
23632
|
+
return;
|
|
23633
|
+
}
|
|
23634
|
+
if (this._frame === undefined)
|
|
23635
|
+
this._startFrames();
|
|
23619
23636
|
}
|
|
23620
|
-
|
|
23637
|
+
_applyZoom(zoom) {
|
|
23621
23638
|
const map = this._map;
|
|
23622
|
-
const
|
|
23623
|
-
const
|
|
23624
|
-
|
|
23625
|
-
|
|
23626
|
-
const d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2;
|
|
23627
|
-
const d4 = snap ? Math.ceil(d3 / snap) * snap : d3;
|
|
23628
|
-
const delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
|
|
23629
|
-
this._delta = 0;
|
|
23630
|
-
this._startTime = null;
|
|
23631
|
-
if (!delta)
|
|
23639
|
+
const anchor = this._anchor;
|
|
23640
|
+
const anchorLatLng = this._anchorLatLng;
|
|
23641
|
+
if (!anchor || !anchorLatLng) {
|
|
23642
|
+
map._move(map.getCenter(), zoom);
|
|
23632
23643
|
return;
|
|
23633
|
-
|
|
23634
|
-
|
|
23635
|
-
|
|
23636
|
-
|
|
23644
|
+
}
|
|
23645
|
+
const viewHalf = map.getSize().divideBy(2);
|
|
23646
|
+
const offset = anchor.subtract(viewHalf);
|
|
23647
|
+
const center = map.unproject(map.project(anchorLatLng, zoom).subtract(offset), zoom);
|
|
23648
|
+
map._move(center, zoom, { round: false });
|
|
23649
|
+
}
|
|
23650
|
+
_startFrames() {
|
|
23651
|
+
this._lastFrameTime = 0;
|
|
23652
|
+
this._frame = requestAnimationFrame((ts) => this._tick(ts));
|
|
23653
|
+
}
|
|
23654
|
+
_stopFrames() {
|
|
23655
|
+
if (this._frame !== undefined) {
|
|
23656
|
+
cancelAnimationFrame(this._frame);
|
|
23657
|
+
this._frame = undefined;
|
|
23658
|
+
}
|
|
23659
|
+
}
|
|
23660
|
+
_tick(timestamp) {
|
|
23661
|
+
const map = this._map;
|
|
23662
|
+
const target = this._targetZoom;
|
|
23663
|
+
if (target === null) {
|
|
23664
|
+
this._frame = undefined;
|
|
23665
|
+
return;
|
|
23666
|
+
}
|
|
23667
|
+
const dt = this._lastFrameTime ? Math.min((timestamp - this._lastFrameTime) / 1000, 0.1) : 1 / 60;
|
|
23668
|
+
this._lastFrameTime = timestamp;
|
|
23669
|
+
const smoothing = Math.max(0.01, map.options.wheelSmoothing);
|
|
23670
|
+
const t = 1 - Math.exp(-dt / smoothing);
|
|
23671
|
+
const current = map.getZoom();
|
|
23672
|
+
const remaining = target - current;
|
|
23673
|
+
if (Math.abs(remaining) < 0.001) {
|
|
23674
|
+
this._applyZoom(target);
|
|
23675
|
+
this._frame = undefined;
|
|
23676
|
+
this._settle();
|
|
23677
|
+
return;
|
|
23678
|
+
}
|
|
23679
|
+
this._applyZoom(current + remaining * t);
|
|
23680
|
+
this._frame = requestAnimationFrame((ts) => this._tick(ts));
|
|
23681
|
+
}
|
|
23682
|
+
_scheduleSettle() {
|
|
23683
|
+
this._stopFrames();
|
|
23684
|
+
this._frame = requestAnimationFrame(() => {
|
|
23685
|
+
this._frame = undefined;
|
|
23686
|
+
setTimeout(() => {
|
|
23687
|
+
if (this._frame === undefined && this._active)
|
|
23688
|
+
this._settle();
|
|
23689
|
+
}, 120);
|
|
23690
|
+
});
|
|
23691
|
+
}
|
|
23692
|
+
_settle() {
|
|
23693
|
+
if (!this._active)
|
|
23694
|
+
return;
|
|
23695
|
+
this._active = false;
|
|
23696
|
+
this._targetZoom = null;
|
|
23697
|
+
this._anchor = undefined;
|
|
23698
|
+
this._anchorLatLng = undefined;
|
|
23699
|
+
this._map._moveEnd(true);
|
|
23637
23700
|
}
|
|
23638
23701
|
}
|
|
23639
23702
|
TsMap.addInitHook("addHandler", "scrollWheelZoom", ScrollWheelZoomHandler);
|