leaflet
Version:
JavaScript library for mobile-friendly interactive maps
109 lines (79 loc) • 3.12 kB
JavaScript
/*
* L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
*/
L.Map.mergeOptions({
touchZoom: L.Browser.touch && !L.Browser.android23,
bounceAtZoomLimits: true
});
L.Map.TouchZoom = L.Handler.extend({
addHooks: function () {
L.DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this);
},
removeHooks: function () {
L.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();
L.DomEvent
.on(document, 'touchmove', this._onTouchMove, this)
.on(document, 'touchend', this._onTouchEnd, this);
L.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.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).subtract(delta));
}
if (!map.options.bounceAtZoomLimits) {
if ((this._zoom <= map.getMinZoom() && scale < 1) ||
(this._zoom >= map.getMaxZoom() && scale > 1)) { return; }
}
if (!this._moved) {
map._moveStart(true);
this._moved = true;
}
L.Util.cancelAnimFrame(this._animRequest);
var moveFn = L.bind(map._move, map, this._center, this._zoom, {pinch: true, round: false});
this._animRequest = L.Util.requestAnimFrame(moveFn, this, true);
L.DomEvent.preventDefault(e);
},
_onTouchEnd: function () {
if (!this._moved || !this._zooming) {
this._zooming = false;
return;
}
this._zooming = false;
L.Util.cancelAnimFrame(this._animRequest);
L.DomEvent
.off(document, 'touchmove', this._onTouchMove)
.off(document, 'touchend', this._onTouchEnd);
var zoom = this._zoom;
zoom = this._map._limitZoom(zoom - this._startZoom > 0 ? Math.ceil(zoom) : Math.floor(zoom));
this._map._animateZoom(this._center, zoom, true, true);
}
});
L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom);