maplair
Version:
JavaScript library for mobile-friendly interactive maps
133 lines (132 loc) • 5.3 kB
JavaScript
import { Point } from "../../geometry/Point.js";
import { Handler } from "../../core/Handler.js";
import { Draggable } from "../../dom/Draggable.js";
import { Bounds } from "../../geometry/Bounds.js";
import { LatLngBounds } from "../../geo/LatLngBounds.js";
//#region src/map/handler/DragHandler.js
var DragHandler = class DragHandler extends Handler {
constructor(...args) {
super(...args);
this.callInitHooks(DragHandler);
}
addHooks() {
if (!this._draggable) {
const map = this._map;
this._draggable = new Draggable(map._mapPane, map._container);
this._draggable.on({
dragstart: this._onDragStart,
drag: this._onDrag,
dragend: this._onDragEnd
}, this);
this._draggable.on("predrag", this._onPreDragLimit, this);
if (map.options.worldCopyJump) {
this._draggable.on("predrag", this._onPreDragWrap, this);
map.on("zoomend", this._onZoomEnd, this);
map.whenReady(this._onZoomEnd, this);
}
}
this._map._container.classList.add("maplair-grab", "maplair-touch-drag");
this._draggable.enable();
this._positions = [];
this._times = [];
}
removeHooks() {
this._map._container.classList.remove("maplair-grab", "maplair-touch-drag");
this._draggable.disable();
}
moved() {
return this._draggable?._moved;
}
moving() {
return this._draggable?._moving;
}
_onDragStart() {
const map = this._map;
map._stop();
if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
const bounds = new LatLngBounds(this._map.options.maxBounds);
this._offsetLimit = new Bounds(this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1), this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1).add(this._map.getSize()));
this._viscosity = Math.min(1, Math.max(0, this._map.options.maxBoundsViscosity));
} else this._offsetLimit = null;
map.fire("movestart").fire("dragstart");
if (map.options.inertia) {
this._positions = [];
this._times = [];
}
}
_onDrag(e) {
if (this._map.options.inertia) {
const time = this._lastTime = Date.now(), pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
this._positions.push(pos);
this._times.push(time);
this._prunePositions(time);
}
this._map.fire("move", e).fire("drag", e);
}
_prunePositions(time) {
while (this._positions.length > 1 && time - this._times[0] > 50) {
this._positions.shift();
this._times.shift();
}
}
_onZoomEnd() {
const pxCenter = this._map.getSize().divideBy(2), pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
}
_viscousLimit(value, threshold) {
return value - (value - threshold) * this._viscosity;
}
_onPreDragLimit() {
if (!this._viscosity || !this._offsetLimit) return;
const offset = this._draggable._newPos.subtract(this._draggable._startPos);
const limit = this._offsetLimit;
if (offset.x < limit.min.x) offset.x = this._viscousLimit(offset.x, limit.min.x);
if (offset.y < limit.min.y) offset.y = this._viscousLimit(offset.y, limit.min.y);
if (offset.x > limit.max.x) offset.x = this._viscousLimit(offset.x, limit.max.x);
if (offset.y > limit.max.y) offset.y = this._viscousLimit(offset.y, limit.max.y);
this._draggable._newPos = this._draggable._startPos.add(offset);
}
_onPreDragWrap() {
const worldWidth = this._worldWidth, halfWidth = Math.round(worldWidth / 2), dx = this._initialWorldOffset, x = this._draggable._newPos.x, newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx, newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx, newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
this._draggable._absPos = new Point(this._draggable._newPos);
this._draggable._newPos.x = newX;
}
_onDragEnd(e) {
const map = this._map, options = map.options, noInertia = !options.inertia || e.noInertia || this._times.length < 2;
map.fire("dragend", e);
if (noInertia) map.fire("moveend");
else {
this._prunePositions(Date.now());
const direction = this._lastPos.subtract(this._positions[0]), duration = (this._lastTime - this._times[0]) / 1e3, ease = options.easeLinearity, speedVector = direction.multiplyBy(ease / duration), speed = speedVector.distanceTo([0, 0]), limitedSpeed = Math.min(options.inertiaMaxSpeed, speed), limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed), decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease);
let offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
if (!offset.x && !offset.y) map.fire("moveend");
else {
offset = map._limitOffset(offset, map.options.maxBounds);
requestAnimationFrame(() => {
map.panBy(offset, {
duration: decelerationDuration,
easeLinearity: ease,
noMoveStart: true,
animate: true
});
});
}
}
}
static register(MaplairMap) {
MaplairMap.mergeOptions({
dragging: true,
inertia: true,
inertiaDeceleration: 3400,
inertiaMaxSpeed: Infinity,
easeLinearity: .2,
worldCopyJump: false,
maxBoundsViscosity: 0
});
MaplairMap.addInitHook("addHandler", "dragging", DragHandler);
}
};
//#endregion
export { DragHandler };
//# sourceMappingURL=DragHandler.js.map