Skip to content

Commit

Permalink
Support disable() mid drag or rotate gesture
Browse files Browse the repository at this point in the history
  • Loading branch information
jfirebaugh committed Feb 26, 2018
1 parent ccf32ee commit 9e775f4
Show file tree
Hide file tree
Showing 4 changed files with 307 additions and 46 deletions.
109 changes: 82 additions & 27 deletions src/ui/handler/drag_pan.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const util = require('../../util/util');
const window = require('../../util/window');
const browser = require('../../util/browser');
const {Event} = require('../../util/evented');
const assert = require('assert');

import type Map from '../map';
import type Point from '@mapbox/point-geometry';
Expand All @@ -22,8 +23,7 @@ const inertiaLinearity = 0.3,
class DragPanHandler {
_map: Map;
_el: HTMLElement;
_enabled: boolean;
_active: boolean;
_state: 'disabled' | 'enabled' | 'pending' | 'active';
_pos: Point;
_previousPos: Point;
_inertia: Array<[number, Point]>;
Expand All @@ -35,6 +35,7 @@ class DragPanHandler {
constructor(map: Map) {
this._map = map;
this._el = map.getCanvasContainer();
this._state = 'disabled';

util.bindAll([
'_onMove',
Expand All @@ -51,7 +52,7 @@ class DragPanHandler {
* @returns {boolean} `true` if the "drag to pan" interaction is enabled.
*/
isEnabled() {
return !!this._enabled;
return this._state !== 'disabled';
}

/**
Expand All @@ -60,7 +61,7 @@ class DragPanHandler {
* @returns {boolean} `true` if the "drag to pan" interaction is active.
*/
isActive() {
return !!this._active;
return this._state === 'active';
}

/**
Expand All @@ -72,7 +73,7 @@ class DragPanHandler {
enable() {
if (this.isEnabled()) return;
this._el.classList.add('mapboxgl-touch-drag-pan');
this._enabled = true;
this._state = 'enabled';
}

/**
Expand All @@ -84,11 +85,26 @@ class DragPanHandler {
disable() {
if (!this.isEnabled()) return;
this._el.classList.remove('mapboxgl-touch-drag-pan');
this._enabled = false;
switch (this._state) {
case 'active':
this._state = 'disabled';
this._unbind();
this._deactivate();
this._fireEvent('dragend');
this._fireEvent('moveend');
break;
case 'pending':
this._state = 'disabled';
this._unbind();
break;
default:
this._state = 'disabled';
break;
}
}

onMouseDown(e: MouseEvent) {
if (!this.isEnabled() || this.isActive()) return;
if (this._state !== 'enabled') return;
if (e.ctrlKey || DOM.mouseButton(e) !== 0) return;

// Bind window-level event listeners for mousemove/up events. In the absence of
Expand All @@ -103,7 +119,7 @@ class DragPanHandler {
}

onTouchStart(e: TouchEvent) {
if (!this.isEnabled() || this.isActive()) return;
if (this._state !== 'enabled') return;
if (e.touches.length > 1) return;

// Bind window-level event listeners for touchmove/end events. In the absence of
Expand All @@ -122,7 +138,7 @@ class DragPanHandler {
// isn't in focus, dragging will continue even though the mouse is no longer pressed.
window.addEventListener('blur', this._onBlur);

this._active = false;
this._state = 'pending';
this._previousPos = DOM.mousePos(this._el, e);
this._inertia = [[browser.now(), this._previousPos]];
}
Expand All @@ -135,10 +151,10 @@ class DragPanHandler {
this._drainInertiaBuffer();
this._inertia.push([browser.now(), this._pos]);

if (!this.isActive()) {
if (this._state === 'pending') {
// we treat the first move event (rather than the mousedown event)
// as the start of the drag
this._active = true;
this._state = 'active';
this._fireEvent('dragstart', e);
this._fireEvent('movestart', e);
}
Expand All @@ -164,43 +180,82 @@ class DragPanHandler {

_onMouseUp(e: MouseEvent) {
if (DOM.mouseButton(e) !== 0) return;
this._finish(e);
switch (this._state) {
case 'active':
this._state = 'enabled';
DOM.suppressClick();
this._unbind();
this._deactivate();
this._inertialPan(e);
break;
case 'pending':
this._state = 'enabled';
this._unbind();
break;
default:
assert(false);
break;
}
}

_onTouchEnd(e: TouchEvent) {
this._finish(e);
switch (this._state) {
case 'active':
this._state = 'enabled';
this._unbind();
this._deactivate();
this._inertialPan(e);
break;
case 'pending':
this._state = 'enabled';
this._unbind();
break;
default:
assert(false);
break;
}
}

_onBlur(e: FocusEvent) {
this._finish(e);
switch (this._state) {
case 'active':
this._state = 'enabled';
this._unbind();
this._deactivate();
this._fireEvent('dragend', e);
this._fireEvent('moveend', e);
break;
case 'pending':
this._state = 'enabled';
this._unbind();
break;
default:
assert(false);
break;
}
}

_finish(e: MouseEvent | TouchEvent | FocusEvent) {
_unbind() {
window.document.removeEventListener('touchmove', this._onMove, {capture: true});
window.document.removeEventListener('touchend', this._onTouchEnd);
window.document.removeEventListener('mousemove', this._onMove, {capture: true});
window.document.removeEventListener('mouseup', this._onMouseUp);
window.removeEventListener('blur', this._onBlur);
}

if (!this.isActive()) return;

this._active = false;
_deactivate() {
delete this._lastMoveEvent;
delete this._previousPos;
delete this._pos;
}

DOM.suppressClick();

_inertialPan(e: MouseEvent | TouchEvent) {
this._fireEvent('dragend', e);
this._drainInertiaBuffer();

const finish = () => {
this._fireEvent('moveend', e);
};

this._drainInertiaBuffer();
const inertia = this._inertia;
if (inertia.length < 2) {
finish();
this._fireEvent('moveend', e);
return;
}

Expand All @@ -210,7 +265,7 @@ class DragPanHandler {
flingDuration = (last[0] - first[0]) / 1000;

if (flingDuration === 0 || last[1].equals(first[1])) {
finish();
this._fireEvent('moveend', e);
return;
}

Expand Down
88 changes: 69 additions & 19 deletions src/ui/handler/drag_rotate.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const util = require('../../util/util');
const window = require('../../util/window');
const browser = require('../../util/browser');
const {Event} = require('../../util/evented');
const assert = require('assert');

import type Map from '../map';
import type Point from '@mapbox/point-geometry';
Expand All @@ -22,8 +23,7 @@ const inertiaLinearity = 0.25,
class DragRotateHandler {
_map: Map;
_el: HTMLElement;
_enabled: boolean;
_active: boolean;
_state: 'disabled' | 'enabled' | 'pending' | 'active';
_button: 'right' | 'left';
_eventButton: number;
_bearingSnap: number;
Expand Down Expand Up @@ -51,6 +51,7 @@ class DragRotateHandler {
}) {
this._map = map;
this._el = options.element || map.getCanvasContainer();
this._state = 'disabled';
this._button = options.button || 'right';
this._bearingSnap = options.bearingSnap || 0;
this._pitchWithRotate = options.pitchWithRotate !== false;
Expand All @@ -69,7 +70,7 @@ class DragRotateHandler {
* @returns {boolean} `true` if the "drag to rotate" interaction is enabled.
*/
isEnabled() {
return !!this._enabled;
return this._state !== 'disabled';
}

/**
Expand All @@ -78,7 +79,7 @@ class DragRotateHandler {
* @returns {boolean} `true` if the "drag to rotate" interaction is active.
*/
isActive() {
return !!this._active;
return this._state === 'active';
}

/**
Expand All @@ -89,7 +90,7 @@ class DragRotateHandler {
*/
enable() {
if (this.isEnabled()) return;
this._enabled = true;
this._state = 'enabled';
}

/**
Expand All @@ -100,11 +101,29 @@ class DragRotateHandler {
*/
disable() {
if (!this.isEnabled()) return;
this._enabled = false;
switch (this._state) {
case 'active':
this._state = 'disabled';
this._unbind();
this._deactivate();
this._fireEvent('rotateend');
if (this._pitchWithRotate) {
this._fireEvent('pitchend');
}
this._fireEvent('moveend');
break;
case 'pending':
this._state = 'disabled';
this._unbind();
break;
default:
this._state = 'disabled';
break;
}
}

onMouseDown(e: MouseEvent) {
if (!this.isEnabled() || this.isActive()) return;
if (this._state !== 'enabled') return;

if (this._button === 'right') {
this._eventButton = DOM.mouseButton(e);
Expand All @@ -128,7 +147,7 @@ class DragRotateHandler {
// isn't in focus, dragging will continue even though the mouse is no longer pressed.
window.addEventListener('blur', this._onBlur);

this._active = false;
this._state = 'pending';
this._inertia = [[browser.now(), this._map.getBearing()]];
this._previousPos = DOM.mousePos(this._el, e);
this._center = this._map.transform.centerPoint; // Center of rotation
Expand All @@ -140,8 +159,8 @@ class DragRotateHandler {
this._lastMoveEvent = e;
this._pos = DOM.mousePos(this._el, e);

if (!this.isActive()) {
this._active = true;
if (this._state === 'pending') {
this._state = 'active';
this._fireEvent('rotatestart', e);
this._fireEvent('movestart', e);
if (this._pitchWithRotate) {
Expand Down Expand Up @@ -183,28 +202,59 @@ class DragRotateHandler {

_onMouseUp(e: MouseEvent) {
if (DOM.mouseButton(e) !== this._eventButton) return;
this._finish(e);
switch (this._state) {
case 'active':
this._state = 'enabled';
DOM.suppressClick();
this._unbind();
this._deactivate();
this._inertialRotate(e);
break;
case 'pending':
this._state = 'enabled';
this._unbind();
break;
default:
assert(false);
break;
}
}

_onBlur(e: FocusEvent) {
this._finish(e);
switch (this._state) {
case 'active':
this._state = 'enabled';
this._unbind();
this._deactivate();
this._fireEvent('rotateend', e);
if (this._pitchWithRotate) {
this._fireEvent('pitchend', e);
}
this._fireEvent('moveend', e);
break;
case 'pending':
this._state = 'enabled';
this._unbind();
break;
default:
assert(false);
break;
}
}

_finish(e: MouseEvent | FocusEvent) {
_unbind() {
window.document.removeEventListener('mousemove', this._onMouseMove, {capture: true});
window.document.removeEventListener('mouseup', this._onMouseUp);
window.removeEventListener('blur', this._onBlur);

DOM.enableDrag();
}

if (!this.isActive()) return;

this._active = false;
_deactivate() {
delete this._lastMoveEvent;
delete this._previousPos;
}

DOM.suppressClick();

_inertialRotate(e: MouseEvent) {
this._fireEvent('rotateend', e);
this._drainInertiaBuffer();

Expand Down
Loading

0 comments on commit 9e775f4

Please sign in to comment.