Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add optional visualizePitch option for NavigationControl and add touch support for pitching with compass icon #8296

Merged
merged 9 commits into from
Jul 2, 2019
19 changes: 19 additions & 0 deletions src/ui/camera.js
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,25 @@ class Camera extends Evented {
return this;
}

/**
* Rotates and pitches the map so that north is up (0° bearing) and pitch is 0°, with an animated transition.
*
* @memberof Map#
* @param options
* @param eventData Additional properties to be added to event objects of events triggered by this method.
* @fires movestart
* @fires moveend
* @returns {Map} `this`
*/
resetNorthPitch(options?: AnimationOptions, eventData?: Object) {
this.easeTo(extend({
bearing: 0,
pitch: 0,
duration: 1000
}, options), eventData);
return this;
}

/**
* Snaps the map so that north is up (0° bearing), if the current bearing is close enough to it (i.e. within the
* `bearingSnap` threshold).
Expand Down
28 changes: 24 additions & 4 deletions src/ui/control/navigation_control.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import type Map from '../map';

type Options = {
showCompass?: boolean,
showZoom?: boolean
showZoom?: boolean,
visualizePitch?: boolean
};

const defaultOptions: Options = {
showCompass: true,
showZoom: true
showZoom: true,
visualizePitch: false
};

/**
Expand All @@ -23,6 +25,7 @@ const defaultOptions: Options = {
* @param {Object} [options]
* @param {Boolean} [options.showCompass=true] If `true` the compass button is included.
* @param {Boolean} [options.showZoom=true] If `true` the zoom-in and zoom-out buttons are included.
* @param {Boolean} [options.visualizePitch=false] If `true` the pitch is visualized by rotating Y-axis of compass.
* @example
* var nav = new mapboxgl.NavigationControl();
* map.addControl(nav, 'top-left');
Expand Down Expand Up @@ -56,7 +59,13 @@ class NavigationControl {
bindAll([
'_rotateCompassArrow'
], this);
this._compass = this._createButton('mapboxgl-ctrl-icon mapboxgl-ctrl-compass', 'Reset bearing to north', () => this._map.resetNorth());
this._compass = this._createButton('mapboxgl-ctrl-icon mapboxgl-ctrl-compass', 'Reset bearing to north', () => {
if (this.options.visualizePitch) {
this._map.resetNorthPitch();
} else {
this._map.resetNorth();
}
});
this._compassArrow = DOM.create('span', 'mapboxgl-ctrl-compass-arrow', this._compass);
}
}
Expand All @@ -68,7 +77,10 @@ class NavigationControl {
}

_rotateCompassArrow() {
const rotate = `rotate(${this._map.transform.angle * (180 / Math.PI)}deg)`;
const rotate = this.options.visualizePitch ?
`rotateX(${this._map.transform.pitch}deg) rotateZ(${this._map.transform.angle * (180 / Math.PI)}deg)` :
`rotate(${this._map.transform.angle}deg)`;

this._compassArrow.style.transform = rotate;
}

Expand All @@ -79,10 +91,14 @@ class NavigationControl {
this._updateZoomButtons();
}
if (this.options.showCompass) {
if (this.options.visualizePitch) {
this._map.on('pitch', this._rotateCompassArrow);
}
this._map.on('rotate', this._rotateCompassArrow);
this._rotateCompassArrow();
this._handler = new DragRotateHandler(map, {button: 'left', element: this._compass});
DOM.addEventListener(this._compass, 'mousedown', this._handler.onMouseDown);
DOM.addEventListener(this._compass, 'touchstart', this._handler.onMouseDown, { passive: false });
this._handler.enable();
}
return this._container;
Expand All @@ -94,8 +110,12 @@ class NavigationControl {
this._map.off('zoom', this._updateZoomButtons);
}
if (this.options.showCompass) {
if (this.options.visualizePitch) {
this._map.off('pitch', this._rotateCompassArrow);
}
this._map.off('rotate', this._rotateCompassArrow);
DOM.removeEventListener(this._compass, 'mousedown', this._handler.onMouseDown);
DOM.removeEventListener(this._compass, 'touchstart', this._handler.onMouseDown, { passive: false });
this._handler.disable();
delete this._handler;
}
Expand Down
8 changes: 5 additions & 3 deletions src/ui/handler/drag_pan.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class DragPanHandler {
_state: 'disabled' | 'enabled' | 'pending' | 'active';
_startPos: Point;
_mouseDownPos: Point;
_prevPos: Point;
_lastPos: Point;
_lastMoveEvent: MouseEvent | TouchEvent | void;
_inertia: Array<[number, Point]>;
Expand Down Expand Up @@ -145,7 +146,7 @@ class DragPanHandler {
window.addEventListener('blur', this._onBlur);

this._state = 'pending';
this._startPos = this._mouseDownPos = this._lastPos = DOM.mousePos(this._el, e);
this._startPos = this._mouseDownPos = this._prevPos = this._lastPos = DOM.mousePos(this._el, e);
this._inertia = [[browser.now(), this._startPos]];
}

Expand Down Expand Up @@ -185,11 +186,11 @@ class DragPanHandler {
const e = this._lastMoveEvent;
if (!e) return;
const tr = this._map.transform;
tr.setLocationAtPoint(tr.pointLocation(this._startPos), this._lastPos);
tr.setLocationAtPoint(tr.pointLocation(this._prevPos), this._lastPos);
this._fireEvent('drag', e);
this._fireEvent('move', e);

this._startPos = this._lastPos;
this._prevPos = this._lastPos;
delete this._lastMoveEvent;
}

Expand Down Expand Up @@ -265,6 +266,7 @@ class DragPanHandler {
}
delete this._lastMoveEvent;
delete this._startPos;
delete this._prevPos;
delete this._mouseDownPos;
delete this._lastPos;
}
Expand Down
44 changes: 33 additions & 11 deletions src/ui/handler/drag_rotate.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ class DragRotateHandler {
_pitchWithRotate: boolean;

_startPos: Point;
_prevPos: Point;
_lastPos: Point;
_startTime: number;
_lastMoveEvent: MouseEvent;
_inertia: Array<[number, number]>;
_center: Point;
Expand Down Expand Up @@ -128,12 +130,18 @@ class DragRotateHandler {
onMouseDown(e: MouseEvent) {
if (this._state !== 'enabled') return;

if (this._button === 'right') {
this._eventButton = DOM.mouseButton(e);
if (this._eventButton !== (e.ctrlKey ? 0 : 2)) return;
const touchEvent = e.type === 'touchstart';

if (touchEvent) {
this._startTime = Date.now();
} else {
if (e.ctrlKey || DOM.mouseButton(e) !== 0) return;
this._eventButton = 0;
if (this._button === 'right') {
this._eventButton = DOM.mouseButton(e);
if (this._eventButton !== (e.ctrlKey ? 0 : 2)) return;
} else {
if (e.ctrlKey || DOM.mouseButton(e) !== 0) return;
this._eventButton = 0;
}
}

DOM.disableDrag();
Expand All @@ -143,16 +151,21 @@ class DragRotateHandler {
// window-level event listeners give us the best shot at capturing events that
// fall outside the map canvas element. Use `{capture: true}` for the move event
// to prevent map move events from being fired during a drag.
window.document.addEventListener('mousemove', this._onMouseMove, {capture: true});
window.document.addEventListener('mouseup', this._onMouseUp);
if (touchEvent) {
window.document.addEventListener('touchmove', this._onMouseMove, { capture: true });
window.document.addEventListener('touchend', this._onMouseUp);
} else {
window.document.addEventListener('mousemove', this._onMouseMove, { capture: true });
window.document.addEventListener('mouseup', this._onMouseUp);
}

// Deactivate when the window loses focus. Otherwise if a mouseup occurs when the window
// isn't in focus, dragging will continue even though the mouse is no longer pressed.
window.addEventListener('blur', this._onBlur);

this._state = 'pending';
this._inertia = [[browser.now(), this._map.getBearing()]];
this._startPos = this._lastPos = DOM.mousePos(this._el, e);
this._startPos = this._prevPos = this._lastPos = DOM.mousePos(this._el, e);
this._center = this._map.transform.centerPoint; // Center of rotation

e.preventDefault();
Expand Down Expand Up @@ -188,7 +201,7 @@ class DragRotateHandler {
if (!e) return;
const tr = this._map.transform;

const p1 = this._startPos,
const p1 = this._prevPos,
p2 = this._lastPos,
bearingDiff = (p1.x - p2.x) * 0.8,
pitchDiff = (p1.y - p2.y) * -0.5,
Expand All @@ -210,10 +223,16 @@ class DragRotateHandler {
this._fireEvent('move', e);

delete this._lastMoveEvent;
this._startPos = this._lastPos;
this._prevPos = this._lastPos;
}

_onMouseUp(e: MouseEvent) {
const touchEvent = e.type === 'touchend';

if (touchEvent && (this._startPos === this._lastPos) && (Date.now() - this._startTime) < 300) {
this._el.click();
}

if (DOM.mouseButton(e) !== this._eventButton) return;
switch (this._state) {
case 'active':
Expand Down Expand Up @@ -256,8 +275,10 @@ class DragRotateHandler {
}

_unbind() {
window.document.removeEventListener('mousemove', this._onMouseMove, {capture: true});
window.document.removeEventListener('mousemove', this._onMouseMove, { capture: true });
window.document.removeEventListener('mouseup', this._onMouseUp);
window.document.removeEventListener('touchmove', this._onMouseMove, { capture: true });
window.document.removeEventListener('touchend', this._onMouseUp);
window.removeEventListener('blur', this._onBlur);
DOM.enableDrag();
}
Expand All @@ -269,6 +290,7 @@ class DragRotateHandler {
}
delete this._lastMoveEvent;
delete this._startPos;
delete this._prevPos;
delete this._lastPos;
}

Expand Down