Skip to content

Commit

Permalink
refactor and expand gesture handling (#9365)
Browse files Browse the repository at this point in the history
Co-authored-by: Anjana Vakil <anjana.vakil@mapbox.com>

- refactor gesture handling to allow multiple handlers to run simultaneously
- add a pitch gesture
- add a tap-drag-zoom gesture
- fix various bugs (see PR)
  • Loading branch information
ansis authored Apr 7, 2020
1 parent 6088941 commit 164b0be
Show file tree
Hide file tree
Showing 44 changed files with 2,595 additions and 2,102 deletions.
2 changes: 1 addition & 1 deletion bench/benchmarks/paint.js
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default class Paint extends Benchmark {
for (const map of this.maps) { for (const map of this.maps) {
map._styleDirty = true; map._styleDirty = true;
map._sourcesDirty = true; map._sourcesDirty = true;
map._render(); map._render(Date.now());
} }
} }


Expand Down
202 changes: 0 additions & 202 deletions src/ui/bind_handlers.js

This file was deleted.

99 changes: 79 additions & 20 deletions src/ui/camera.js
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import LngLat from '../geo/lng_lat';
import LngLatBounds from '../geo/lng_lat_bounds'; import LngLatBounds from '../geo/lng_lat_bounds';
import Point from '@mapbox/point-geometry'; import Point from '@mapbox/point-geometry';
import {Event, Evented} from '../util/evented'; import {Event, Evented} from '../util/evented';
import assert from 'assert';
import {Debug} from '../util/debug';


import type Transform from '../geo/transform'; import type Transform from '../geo/transform';
import type {LngLatLike} from '../geo/lng_lat'; import type {LngLatLike} from '../geo/lng_lat';
Expand Down Expand Up @@ -91,9 +93,10 @@ class Camera extends Evented {
_easeEndTimeoutID: TimeoutID; _easeEndTimeoutID: TimeoutID;
_easeStart: number; _easeStart: number;
_easeOptions: {duration: number, easing: (_: number) => number}; _easeOptions: {duration: number, easing: (_: number) => number};
_easeId: string | void;


_onEaseFrame: (_: number) => void; _onEaseFrame: (_: number) => void;
_onEaseEnd: () => void; _onEaseEnd: (easeId?: string) => void;
_easeFrameId: ?TaskID; _easeFrameId: ?TaskID;


+_requestRenderFrame: (() => void) => TaskID; +_requestRenderFrame: (() => void) => TaskID;
Expand All @@ -107,6 +110,8 @@ class Camera extends Evented {
this._bearingSnap = options.bearingSnap; this._bearingSnap = options.bearingSnap;


bindAll(['_renderFrameCallback'], this); bindAll(['_renderFrameCallback'], this);

//addAssertions(this);
} }


/** /**
Expand Down Expand Up @@ -708,8 +713,8 @@ class Camera extends Evented {
* @returns {Map} `this` * @returns {Map} `this`
* @see [Navigate the map with game-like controls](https://www.mapbox.com/mapbox-gl-js/example/game-controls/) * @see [Navigate the map with game-like controls](https://www.mapbox.com/mapbox-gl-js/example/game-controls/)
*/ */
easeTo(options: CameraOptions & AnimationOptions & {delayEndEvents?: number}, eventData?: Object) { easeTo(options: CameraOptions & AnimationOptions & {easeId?: string}, eventData?: Object) {
this.stop(); this._stop(false, options.easeId);


options = extend({ options = extend({
offset: [0, 0], offset: [0, 0],
Expand Down Expand Up @@ -747,12 +752,20 @@ class Camera extends Evented {
aroundPoint = tr.locationPoint(around); aroundPoint = tr.locationPoint(around);
} }


this._zooming = (zoom !== startZoom); const currently = {
this._rotating = (startBearing !== bearing); moving: this._moving,
this._pitching = (pitch !== startPitch); zooming: this._zooming,
rotating: this._rotating,
pitching: this._pitching
};

this._zooming = this._zooming || (zoom !== startZoom);
this._rotating = this._rotating || (startBearing !== bearing);
this._pitching = this._pitching || (pitch !== startPitch);
this._padding = !tr.isPaddingEqual(padding); this._padding = !tr.isPaddingEqual(padding);


this._prepareEase(eventData, options.noMoveStart); this._easeId = options.easeId;
this._prepareEase(eventData, options.noMoveStart, currently);


clearTimeout(this._easeEndTimeoutID); clearTimeout(this._easeEndTimeoutID);


Expand Down Expand Up @@ -787,30 +800,26 @@ class Camera extends Evented {


this._fireMoveEvents(eventData); this._fireMoveEvents(eventData);


}, () => { }, (interruptingEaseId?: string) => {
if (options.delayEndEvents) { this._afterEase(eventData, interruptingEaseId);
this._easeEndTimeoutID = setTimeout(() => this._afterEase(eventData), options.delayEndEvents);
} else {
this._afterEase(eventData);
}
}, options); }, options);


return this; return this;
} }


_prepareEase(eventData?: Object, noMoveStart: boolean) { _prepareEase(eventData?: Object, noMoveStart: boolean, currently: Object = {}) {
this._moving = true; this._moving = true;


if (!noMoveStart) { if (!noMoveStart && !currently.moving) {
this.fire(new Event('movestart', eventData)); this.fire(new Event('movestart', eventData));
} }
if (this._zooming) { if (this._zooming && !currently.zooming) {
this.fire(new Event('zoomstart', eventData)); this.fire(new Event('zoomstart', eventData));
} }
if (this._rotating) { if (this._rotating && !currently.rotating) {
this.fire(new Event('rotatestart', eventData)); this.fire(new Event('rotatestart', eventData));
} }
if (this._pitching) { if (this._pitching && !currently.pitching) {
this.fire(new Event('pitchstart', eventData)); this.fire(new Event('pitchstart', eventData));
} }
} }
Expand All @@ -828,7 +837,14 @@ class Camera extends Evented {
} }
} }


_afterEase(eventData?: Object) { _afterEase(eventData?: Object, easeId?: string) {
// if this easing is being stopped to start another easing with
// the same id then don't fire any events to avoid extra start/stop events
if (this._easeId && easeId && this._easeId === easeId) {
return;
}
delete this._easeId;

const wasZooming = this._zooming; const wasZooming = this._zooming;
const wasRotating = this._rotating; const wasRotating = this._rotating;
const wasPitching = this._pitching; const wasPitching = this._pitching;
Expand Down Expand Up @@ -1078,6 +1094,10 @@ class Camera extends Evented {
* @returns {Map} `this` * @returns {Map} `this`
*/ */
stop(): this { stop(): this {
return this._stop();
}

_stop(allowGestures?: boolean, easeId?: string): this {
if (this._easeFrameId) { if (this._easeFrameId) {
this._cancelRenderFrame(this._easeFrameId); this._cancelRenderFrame(this._easeFrameId);
delete this._easeFrameId; delete this._easeFrameId;
Expand All @@ -1090,7 +1110,11 @@ class Camera extends Evented {
// it unintentionally. // it unintentionally.
const onEaseEnd = this._onEaseEnd; const onEaseEnd = this._onEaseEnd;
delete this._onEaseEnd; delete this._onEaseEnd;
onEaseEnd.call(this); onEaseEnd.call(this, easeId);
}
if (!allowGestures) {
const handlers = (this: any).handlers;
if (handlers) handlers.stop();
} }
return this; return this;
} }
Expand Down Expand Up @@ -1143,4 +1167,39 @@ class Camera extends Evented {
} }
} }


// In debug builds, check that camera change events are fired in the correct order.
// - ___start events needs to be fired before ___ and ___end events
// - another ___start event can't be fired before a ___end event has been fired for the previous one
function addAssertions(camera: Camera) { //eslint-disable-line
Debug.run(() => {
const inProgress = {};

['drag', 'zoom', 'rotate', 'pitch', 'move'].forEach(name => {
inProgress[name] = false;

camera.on(`${name}start`, () => {
assert(!inProgress[name], `"${name}start" fired twice without a "${name}end"`);
inProgress[name] = true;
assert(inProgress.move);
});

camera.on(name, () => {
assert(inProgress[name]);
assert(inProgress.move);
});

camera.on(`${name}end`, () => {
assert(inProgress.move);
assert(inProgress[name]);
inProgress[name] = false;
});
});

// Canary used to test whether this function is stripped in prod build
canary = 'canary debug run';
});
}

let canary; //eslint-disable-line

export default Camera; export default Camera;
Loading

0 comments on commit 164b0be

Please sign in to comment.