UNPKG

leaflet-rotate

Version:

A Leaflet plugin that allows to add rotation functionality to map tiles

178 lines (141 loc) 6.06 kB
/** * TouchGestures is both TouchZoom plus TouchRotate * * @see https://github.com/fnicollet/Leaflet/commit/a77af51a6b10f308d1b9a16552091d1d0aee8834 * @see https://github.com/Leaflet/Leaflet/blob/v1.9.3/src/map/handler/Map.TouchZoom.js * * @typedef L.Map.TouchGestures */ L.Map.mergeOptions({ /** * Set it to false if you don't want the map to * zoom beyond min/max zoom and then bounce back * when pinch-zooming. * * @type {Boolean} */ bounceAtZoomLimits: true, }); L.Map.TouchGestures = L.Handler.extend({ initialize: function(map) { this._map = map; this.rotate = !!this._map.options.touchRotate; this.zoom = !!this._map.options.touchZoom; }, 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 || this._rotating) { return; } var p1 = map.mouseEventToContainerPoint(e.touches[0]), p2 = map.mouseEventToContainerPoint(e.touches[1]), vector = p1.subtract(p2); this._centerPoint = map.getSize()._divideBy(2); this._startLatLng = map.containerPointToLatLng(this._centerPoint); if (this.zoom) { if (map.options.touchZoom !== 'center') { this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2)); } this._startDist = p1.distanceTo(p2); this._startZoom = map.getZoom(); this._zooming = true; } else { this._zooming = false; } if (this.rotate) { this._startTheta = Math.atan(vector.x / vector.y); this._startBearing = map.getBearing(); if (vector.y < 0) { this._startBearing += 180; } this._rotating = true; } else { this._rotating = false; } this._moved = false; map._stop(); L.DomEvent .on(document, 'touchmove', this._onTouchMove, this) .on(document, 'touchend touchcancel', this._onTouchEnd, this); L.DomEvent.preventDefault(e); }, _onTouchMove: function(e) { if (!e.touches || e.touches.length !== 2 || !(this._zooming || this._rotating)) { return; } var map = this._map, p1 = map.mouseEventToContainerPoint(e.touches[0]), p2 = map.mouseEventToContainerPoint(e.touches[1]), vector = p1.subtract(p2), scale = p1.distanceTo(p2) / this._startDist, delta; if (this._rotating) { var theta = Math.atan(vector.x / vector.y); var bearingDelta = (theta - this._startTheta) * L.DomUtil.RAD_TO_DEG; if (vector.y < 0) { bearingDelta += 180; } if (bearingDelta) { /** * @TODO the pivot should be the last touch point, * but zoomAnimation manages to overwrite the rotate * pane position. Maybe related to #3529. * * @see https://github.com/Leaflet/Leaflet/pull/3529 * @see https://github.com/fnicollet/Leaflet/commit/a77af51a6b10f308d1b9a16552091d1d0aee8834 */ map.setBearing(this._startBearing - bearingDelta); } } if (this._zooming) { 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 delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint); if (scale === 1 && delta.x === 0 && delta.y === 0) { return; } var alpha = -map.getBearing() * L.DomUtil.DEG_TO_RAD; this._center = map.unproject(map.project(this._pinchStartLatLng).subtract(delta.rotate(alpha))); } } if (!this._moved) { map._moveStart(true, false); this._moved = true; } L.Util.cancelAnimFrame(this._animRequest); var moveFn = map._move.bind(map, this._center, this._zoom, { pinch: true, round: false }, undefined); this._animRequest = L.Util.requestAnimFrame(moveFn, this, true); L.DomEvent.preventDefault(e); }, _onTouchEnd: function() { if (!this._moved || !(this._zooming || this._rotating)) { this._zooming = false; return; } this._zooming = false; this._rotating = false; L.Util.cancelAnimFrame(this._animRequest); L.DomEvent .off(document, 'touchmove', this._onTouchMove, this) .off(document, 'touchend touchcancel', this._onTouchEnd, this); if (this.zoom) { // 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)); } } }, }); /** * Add Touch Gestures handler (enabled unless `touchGestures` is unset). * * @property {L.Map.TouchGestures} touchGestures */ L.Map.addInitHook('addHandler', 'touchGestures', L.Map.TouchGestures);