leaflet
Version:
JavaScript library for mobile-friendly interactive maps
68 lines (51 loc) • 1.99 kB
JavaScript
import {Handler} from '../../core/Handler.js';
import * as DomEvent from '../../dom/DomEvent.js';
// Enables mouse scroll wheel zoom on the map, enabled by default.
export class ScrollWheelZoomHandler extends Handler {
addHooks() {
DomEvent.on(this._map._container, 'wheel', this._onWheelScroll, this);
/** @type {number} */
this._delta = 0;
}
removeHooks() {
DomEvent.off(this._map._container, 'wheel', this._onWheelScroll, this);
clearTimeout(this._timer);
}
/** @param {WheelEvent} e */
_onWheelScroll(e) {
const delta = DomEvent.getWheelDelta(e);
const debounce = this._map.options.wheelDebounceTime;
this._delta += delta;
// resolving the container point reads layout, so defer it to _performZoom
/** @type {WheelEvent} */
this._lastEvent = e;
if (!this._startTime) {
this._startTime = Date.now();
}
const left = Math.max(debounce - (Date.now() - this._startTime), 0);
clearTimeout(this._timer);
this._timer = setTimeout(this._performZoom.bind(this), left);
DomEvent.stop(e);
}
_performZoom() {
const map = this._map,
zoom = map.getZoom(),
snap = this._map.options.zoomSnap ?? 0;
map._stop(); // stop panning and fly animations if any
// map the delta with a sigmoid function to -4..4 range leaning on -1..1
const d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
// with reduced motion, only zooms by up to one level are animated, so a hard scroll is capped to that
d5 = map._prefersReducedMotion() ? Math.min(d4, 1) : d4,
delta = map._limitZoom(zoom + (this._delta > 0 ? d5 : -d5)) - zoom;
this._delta = 0;
this._startTime = null;
if (!delta || map._zoomInputBlocked()) { return; }
if (map.options.scrollWheelZoom === 'center') {
map.setZoom(zoom + delta);
} else {
map.setZoomAround(map.pointerEventToContainerPoint(this._lastEvent), zoom + delta);
}
}
}