131 lines
4.2 KiB
JavaScript
131 lines
4.2 KiB
JavaScript
import {Map} from '../Map';
|
|
import {Handler} from '../../core/Handler';
|
|
import * as DomEvent from '../../dom/DomEvent';
|
|
import * as Util from '../../core/Util';
|
|
import * as DomUtil from '../../dom/DomUtil';
|
|
import Browser from '../../core/Browser';
|
|
|
|
/*
|
|
* L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
|
|
*/
|
|
|
|
// @namespace Map
|
|
// @section Interaction Options
|
|
Map.mergeOptions({
|
|
// @section Touch interaction options
|
|
// @option touchZoom: Boolean|String = *
|
|
// Whether the map can be zoomed by touch-dragging with two fingers. If
|
|
// passed `'center'`, it will zoom to the center of the view regardless of
|
|
// where the touch events (fingers) were. Enabled for touch-capable web
|
|
// browsers.
|
|
touchZoom: Browser.touch,
|
|
|
|
// @option bounceAtZoomLimits: Boolean = true
|
|
// Set it to false if you don't want the map to zoom beyond min/max zoom
|
|
// and then bounce back when pinch-zooming.
|
|
bounceAtZoomLimits: true
|
|
});
|
|
|
|
export var TouchZoom = Handler.extend({
|
|
addHooks: function () {
|
|
DomUtil.addClass(this._map._container, 'leaflet-touch-zoom');
|
|
DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this);
|
|
},
|
|
|
|
removeHooks: function () {
|
|
DomUtil.removeClass(this._map._container, 'leaflet-touch-zoom');
|
|
DomEvent.off(this._map._container, 'touchstart', this._onTouchStart, this);
|
|
},
|
|
|
|
_onTouchStart: function (e) {
|
|
var map = this._map;
|
|
if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
|
|
|
|
var p1 = map.mouseEventToContainerPoint(e.touches[0]),
|
|
p2 = map.mouseEventToContainerPoint(e.touches[1]);
|
|
|
|
this._centerPoint = map.getSize()._divideBy(2);
|
|
this._startLatLng = map.containerPointToLatLng(this._centerPoint);
|
|
if (map.options.touchZoom !== 'center') {
|
|
this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
|
|
}
|
|
|
|
this._startDist = p1.distanceTo(p2);
|
|
this._startZoom = map.getZoom();
|
|
|
|
this._moved = false;
|
|
this._zooming = true;
|
|
|
|
map._stop();
|
|
|
|
DomEvent.on(document, 'touchmove', this._onTouchMove, this);
|
|
DomEvent.on(document, 'touchend touchcancel', this._onTouchEnd, this);
|
|
|
|
DomEvent.preventDefault(e);
|
|
},
|
|
|
|
_onTouchMove: function (e) {
|
|
if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
|
|
|
|
var map = this._map,
|
|
p1 = map.mouseEventToContainerPoint(e.touches[0]),
|
|
p2 = map.mouseEventToContainerPoint(e.touches[1]),
|
|
scale = p1.distanceTo(p2) / this._startDist;
|
|
|
|
this._zoom = map.getScaleZoom(scale, this._startZoom);
|
|
|
|
if (!map.options.bounceAtZoomLimits && (
|
|
(this._zoom < map.getMinZoom() && scale < 1) ||
|
|
(this._zoom > map.getMaxZoom() && scale > 1))) {
|
|
this._zoom = map._limitZoom(this._zoom);
|
|
}
|
|
|
|
if (map.options.touchZoom === 'center') {
|
|
this._center = this._startLatLng;
|
|
if (scale === 1) { return; }
|
|
} else {
|
|
// Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
|
|
var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
|
|
if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
|
|
this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
|
|
}
|
|
|
|
if (!this._moved) {
|
|
map._moveStart(true, false);
|
|
this._moved = true;
|
|
}
|
|
|
|
Util.cancelAnimFrame(this._animRequest);
|
|
|
|
var moveFn = Util.bind(map._move, map, this._center, this._zoom, {pinch: true, round: false}, undefined);
|
|
this._animRequest = Util.requestAnimFrame(moveFn, this, true);
|
|
|
|
DomEvent.preventDefault(e);
|
|
},
|
|
|
|
_onTouchEnd: function () {
|
|
if (!this._moved || !this._zooming) {
|
|
this._zooming = false;
|
|
return;
|
|
}
|
|
|
|
this._zooming = false;
|
|
Util.cancelAnimFrame(this._animRequest);
|
|
|
|
DomEvent.off(document, 'touchmove', this._onTouchMove, this);
|
|
DomEvent.off(document, 'touchend touchcancel', this._onTouchEnd, this);
|
|
|
|
// Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
|
|
if (this._map.options.zoomAnimation) {
|
|
this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
|
|
} else {
|
|
this._map._resetView(this._center, this._map._limitZoom(this._zoom));
|
|
}
|
|
}
|
|
});
|
|
|
|
// @section Handlers
|
|
// @property touchZoom: Handler
|
|
// Touch zoom handler.
|
|
Map.addInitHook('addHandler', 'touchZoom', TouchZoom);
|