import {Handler} from '../../core/Handler'; import * as DomUtil from '../../dom/DomUtil'; import {Draggable} from '../../dom/Draggable'; import {toBounds} from '../../geometry/Bounds'; import {toPoint} from '../../geometry/Point'; import {requestAnimFrame, cancelAnimFrame} from '../../core/Util'; /* * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable. */ /* @namespace Marker * @section Interaction handlers * * Interaction handlers are properties of a marker instance that allow you to control interaction behavior in runtime, enabling or disabling certain features such as dragging (see `Handler` methods). Example: * * ```js * marker.dragging.disable(); * ``` * * @property dragging: Handler * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)). */ export var MarkerDrag = Handler.extend({ initialize: function (marker) { this._marker = marker; }, addHooks: function () { var icon = this._marker._icon; if (!this._draggable) { this._draggable = new Draggable(icon, icon, true); } this._draggable.on({ dragstart: this._onDragStart, predrag: this._onPreDrag, drag: this._onDrag, dragend: this._onDragEnd }, this).enable(); DomUtil.addClass(icon, 'leaflet-marker-draggable'); }, removeHooks: function () { this._draggable.off({ dragstart: this._onDragStart, predrag: this._onPreDrag, drag: this._onDrag, dragend: this._onDragEnd }, this).disable(); if (this._marker._icon) { DomUtil.removeClass(this._marker._icon, 'leaflet-marker-draggable'); } }, moved: function () { return this._draggable && this._draggable._moved; }, _adjustPan: function (e) { var marker = this._marker, map = marker._map, speed = this._marker.options.autoPanSpeed, padding = this._marker.options.autoPanPadding, iconPos = L.DomUtil.getPosition(marker._icon), bounds = map.getPixelBounds(), origin = map.getPixelOrigin(); var panBounds = toBounds( bounds.min._subtract(origin).add(padding), bounds.max._subtract(origin).subtract(padding) ); if (!panBounds.contains(iconPos)) { // Compute incremental movement var movement = toPoint( (Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) - (Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x), (Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) - (Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y) ).multiplyBy(speed); map.panBy(movement, {animate: false}); this._draggable._newPos._add(movement); this._draggable._startPos._add(movement); L.DomUtil.setPosition(marker._icon, this._draggable._newPos); this._onDrag(e); this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e)); } }, _onDragStart: function () { // @section Dragging events // @event dragstart: Event // Fired when the user starts dragging the marker. // @event movestart: Event // Fired when the marker starts moving (because of dragging). this._oldLatLng = this._marker.getLatLng(); this._marker .closePopup() .fire('movestart') .fire('dragstart'); }, _onPreDrag: function (e) { if (this._marker.options.autoPan) { cancelAnimFrame(this._panRequest); this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e)); } }, _onDrag: function (e) { var marker = this._marker, shadow = marker._shadow, iconPos = DomUtil.getPosition(marker._icon), latlng = marker._map.layerPointToLatLng(iconPos); // update shadow position if (shadow) { DomUtil.setPosition(shadow, iconPos); } marker._latlng = latlng; e.latlng = latlng; e.oldLatLng = this._oldLatLng; // @event drag: Event // Fired repeatedly while the user drags the marker. marker .fire('move', e) .fire('drag', e); }, _onDragEnd: function (e) { // @event dragend: DragEndEvent // Fired when the user stops dragging the marker. cancelAnimFrame(this._panRequest); // @event moveend: Event // Fired when the marker stops moving (because of dragging). delete this._oldLatLng; this._marker .fire('moveend') .fire('dragend', e); } });