UNPKG

leaflet

Version:

JavaScript library for mobile-friendly interactive maps

210 lines (166 loc) 6.39 kB
/* * @class Draggable * @aka L.Draggable * @inherits Evented * * A class for making DOM elements draggable (including touch support). * Used internally for map and marker dragging. Only works for elements * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition). * * @example * ```js * var draggable = new L.Draggable(elementToDrag); * draggable.enable(); * ``` */ L.Draggable = L.Evented.extend({ options: { // @option clickTolerance: Number = 3 // The max number of pixels a user can shift the mouse pointer during a click // for it to be considered a valid click (as opposed to a mouse drag). clickTolerance: 3 }, statics: { START: L.Browser.touch ? ['touchstart', 'mousedown'] : ['mousedown'], END: { mousedown: 'mouseup', touchstart: 'touchend', pointerdown: 'touchend', MSPointerDown: 'touchend' }, MOVE: { mousedown: 'mousemove', touchstart: 'touchmove', pointerdown: 'touchmove', MSPointerDown: 'touchmove' } }, // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline: Boolean) // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default). initialize: function (element, dragStartTarget, preventOutline) { this._element = element; this._dragStartTarget = dragStartTarget || element; this._preventOutline = preventOutline; }, // @method enable() // Enables the dragging ability enable: function () { if (this._enabled) { return; } L.DomEvent.on(this._dragStartTarget, L.Draggable.START.join(' '), this._onDown, this); this._enabled = true; }, // @method disable() // Disables the dragging ability disable: function () { if (!this._enabled) { return; } L.DomEvent.off(this._dragStartTarget, L.Draggable.START.join(' '), this._onDown, this); this._enabled = false; this._moved = false; }, _onDown: function (e) { // Ignore simulated events, since we handle both touch and // mouse explicitly; otherwise we risk getting duplicates of // touch events, see #4315. // Also ignore the event if disabled; this happens in IE11 // under some circumstances, see #3666. if (e._simulated || !this._enabled) { return; } this._moved = false; if (L.DomUtil.hasClass(this._element, 'leaflet-zoom-anim')) { return; } if (L.Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches) || !this._enabled) { return; } L.Draggable._dragging = true; // Prevent dragging multiple objects at once. if (this._preventOutline) { L.DomUtil.preventOutline(this._element); } L.DomUtil.disableImageDrag(); L.DomUtil.disableTextSelection(); if (this._moving) { return; } // @event down: Event // Fired when a drag is about to start. this.fire('down'); var first = e.touches ? e.touches[0] : e; this._startPoint = new L.Point(first.clientX, first.clientY); L.DomEvent .on(document, L.Draggable.MOVE[e.type], this._onMove, this) .on(document, L.Draggable.END[e.type], this._onUp, this); }, _onMove: function (e) { // Ignore simulated events, since we handle both touch and // mouse explicitly; otherwise we risk getting duplicates of // touch events, see #4315. // Also ignore the event if disabled; this happens in IE11 // under some circumstances, see #3666. if (e._simulated || !this._enabled) { return; } if (e.touches && e.touches.length > 1) { this._moved = true; return; } var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e), newPoint = new L.Point(first.clientX, first.clientY), offset = newPoint.subtract(this._startPoint); if (!offset.x && !offset.y) { return; } if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; } L.DomEvent.preventDefault(e); if (!this._moved) { // @event dragstart: Event // Fired when a drag starts this.fire('dragstart'); this._moved = true; this._startPos = L.DomUtil.getPosition(this._element).subtract(offset); L.DomUtil.addClass(document.body, 'leaflet-dragging'); this._lastTarget = e.target || e.srcElement; // IE and Edge do not give the <use> element, so fetch it // if necessary if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) { this._lastTarget = this._lastTarget.correspondingUseElement; } L.DomUtil.addClass(this._lastTarget, 'leaflet-drag-target'); } this._newPos = this._startPos.add(offset); this._moving = true; L.Util.cancelAnimFrame(this._animRequest); this._lastEvent = e; this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true); }, _updatePosition: function () { var e = {originalEvent: this._lastEvent}; // @event predrag: Event // Fired continuously during dragging *before* each corresponding // update of the element's position. this.fire('predrag', e); L.DomUtil.setPosition(this._element, this._newPos); // @event drag: Event // Fired continuously during dragging. this.fire('drag', e); }, _onUp: function (e) { // Ignore simulated events, since we handle both touch and // mouse explicitly; otherwise we risk getting duplicates of // touch events, see #4315. // Also ignore the event if disabled; this happens in IE11 // under some circumstances, see #3666. if (e._simulated || !this._enabled) { return; } L.DomUtil.removeClass(document.body, 'leaflet-dragging'); if (this._lastTarget) { L.DomUtil.removeClass(this._lastTarget, 'leaflet-drag-target'); this._lastTarget = null; } for (var i in L.Draggable.MOVE) { L.DomEvent .off(document, L.Draggable.MOVE[i], this._onMove, this) .off(document, L.Draggable.END[i], this._onUp, this); } L.DomUtil.enableImageDrag(); L.DomUtil.enableTextSelection(); if (this._moved && this._moving) { // ensure drag is not fired after dragend L.Util.cancelAnimFrame(this._animRequest); // @event dragend: DragEndEvent // Fired when the drag ends. this.fire('dragend', { distance: this._newPos.distanceTo(this._startPos) }); } this._moving = false; L.Draggable._dragging = false; } });