Skip to content

Commit

Permalink
options.dragPan can be assigned an object to customize panning inerti…
Browse files Browse the repository at this point in the history
…a ( h/t @aMoniker) (#8887)
  • Loading branch information
Arindam Bose authored Nov 5, 2019
1 parent f8ab95c commit db2f77c
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 17 deletions.
46 changes: 34 additions & 12 deletions src/ui/handler/drag_pan.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow

import DOM from '../../util/dom';
import {bezier, bindAll} from '../../util/util';
import {bezier, bindAll, extend} from '../../util/util';
import window from '../../util/window';
import browser from '../../util/browser';
import {Event} from '../../util/evented';
Expand All @@ -11,10 +11,15 @@ import type Map from '../map';
import type Point from '@mapbox/point-geometry';
import type {TaskID} from '../../util/task_queue';

const inertiaLinearity = 0.3,
inertiaEasing = bezier(0, 0, inertiaLinearity, 1),
inertiaMaxSpeed = 1400, // px/s
inertiaDeceleration = 2500; // px/s^2
const defaultInertia = {
linearity: 0.3,
easing: bezier(0, 0, 0.3, 1),
maxSpeed: 1400,
deceleration: 2500,
};
export type PanInertiaOptions = typeof defaultInertia;

export type DragPanOptions = boolean | PanInertiaOptions;

/**
* The `DragPanHandler` allows the user to pan the map by clicking and dragging
Expand All @@ -35,6 +40,7 @@ class DragPanHandler {
_frameId: ?TaskID;
_clickTolerance: number;
_shouldStart: ?boolean;
_inertiaOptions: PanInertiaOptions;

/**
* @private
Expand All @@ -46,6 +52,7 @@ class DragPanHandler {
this._el = map.getCanvasContainer();
this._state = 'disabled';
this._clickTolerance = options.clickTolerance || 1;
this._inertiaOptions = defaultInertia;

bindAll([
'_onMove',
Expand Down Expand Up @@ -77,13 +84,27 @@ class DragPanHandler {
/**
* Enables the "drag to pan" interaction.
*
* @param {Object} [options]
* @param {number} [options.linearity=0] factor used to scale the drag velocity
* @param {Function} [options.easing=bezier(0, 0, 0.3, 1)] easing function applled to `map.panTo` when applying the drag.
* @param {number} [options.maxSpeed=1400] the maximum value of the drag velocity.
* @param {number} [options.deceleration=2500] the rate at which the speed reduces after the pan ends.
*
* @example
* map.dragPan.enable();
* @example
* map.dragPan.enable();
* map.dragpan.enable({
* linearity: 0.3,
* easing: bezier(0, 0, 0.3, 1),
* maxSpeed: 1400,
* deceleration: 2500,
* });
*/
enable() {
enable(options: DragPanOptions) {
if (this.isEnabled()) return;
this._el.classList.add('mapboxgl-touch-drag-pan');
this._state = 'enabled';
this._inertiaOptions = extend(defaultInertia, options);
}

/**
Expand Down Expand Up @@ -358,22 +379,23 @@ class DragPanHandler {
this._fireEvent('moveend', e);
return;
}
const {linearity, easing, maxSpeed, deceleration} = this._inertiaOptions;

// calculate px/s velocity & adjust for increased initial animation speed when easing out
const velocity = flingOffset.mult(inertiaLinearity / flingDuration);
const velocity = flingOffset.mult(linearity / flingDuration);
let speed = velocity.mag(); // px/s

if (speed > inertiaMaxSpeed) {
speed = inertiaMaxSpeed;
if (speed > maxSpeed) {
speed = maxSpeed;
velocity._unit()._mult(speed);
}

const duration = speed / (inertiaDeceleration * inertiaLinearity),
const duration = speed / (deceleration * linearity),
offset = velocity.mult(-duration / 2);

this._map.panBy(offset, {
duration: duration * 1000,
easing: inertiaEasing,
easing,
noMoveStart: true
}, {originalEvent: e});
}
Expand Down
6 changes: 3 additions & 3 deletions src/ui/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import type {StyleImageInterface} from '../style/style_image';
import type ScrollZoomHandler from './handler/scroll_zoom';
import type BoxZoomHandler from './handler/box_zoom';
import type DragRotateHandler from './handler/drag_rotate';
import type DragPanHandler from './handler/drag_pan';
import type DragPanHandler, {DragPanOptions} from './handler/drag_pan';
import type KeyboardHandler from './handler/keyboard';
import type DoubleClickZoomHandler from './handler/dblclick_zoom';
import type TouchZoomRotateHandler from './handler/touch_zoom_rotate';
Expand Down Expand Up @@ -82,7 +82,7 @@ type MapOptions = {
maxZoom?: ?number,
boxZoom?: boolean,
dragRotate?: boolean,
dragPan?: boolean,
dragPan?: DragPanOptions,
keyboard?: boolean,
doubleClickZoom?: boolean,
touchZoomRotate?: boolean,
Expand Down Expand Up @@ -195,7 +195,7 @@ const defaultOptions = {
* @param {boolean|Object} [options.scrollZoom=true] If `true`, the "scroll to zoom" interaction is enabled. An `Object` value is passed as options to {@link ScrollZoomHandler#enable}.
* @param {boolean} [options.boxZoom=true] If `true`, the "box zoom" interaction is enabled (see {@link BoxZoomHandler}).
* @param {boolean} [options.dragRotate=true] If `true`, the "drag to rotate" interaction is enabled (see {@link DragRotateHandler}).
* @param {boolean} [options.dragPan=true] If `true`, the "drag to pan" interaction is enabled (see {@link DragPanHandler}).
* @param {boolean|Object} [options.dragPan=true] If `true`, the "drag to pan" interaction is enabled. An `Object` value is passed as options to {@link DragPanHandler#enable}.
* @param {boolean} [options.keyboard=true] If `true`, keyboard shortcuts are enabled (see {@link KeyboardHandler}).
* @param {boolean} [options.doubleClickZoom=true] If `true`, the "double click to zoom" interaction is enabled (see {@link DoubleClickZoomHandler}).
* @param {boolean|Object} [options.touchZoomRotate=true] If `true`, the "pinch to rotate and zoom" interaction is enabled. An `Object` value is passed as options to {@link TouchZoomRotateHandler#enable}.
Expand Down
24 changes: 22 additions & 2 deletions test/unit/ui/handler/drag_pan.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@ import window from '../../../../src/util/window';
import Map from '../../../../src/ui/map';
import DOM from '../../../../src/util/dom';
import simulate from '../../../util/simulate_interaction';
import DragPanHandler from '../../../../src/ui/handler/drag_pan';

function createMap(t, clickTolerance) {
function createMap(t, clickTolerance, dragPan) {
t.stub(Map.prototype, '_detectMissingCSS');
return new Map({container: DOM.create('div', '', window.document.body), clickTolerance: clickTolerance || 0});
return new Map({
container: DOM.create('div', '', window.document.body),
clickTolerance: clickTolerance || 0,
dragPan: dragPan || true
});
}

test('DragPanHandler fires dragstart, drag, and dragend events at appropriate times in response to a mouse-triggered drag', (t) => {
Expand Down Expand Up @@ -935,3 +940,18 @@ test('DragPanHandler fires dragstart, drag, dragend events in response to multi-
map.remove();
t.end();
});

test('DragPanHander#enable gets called with dragPan map option parameters', (t) => {
const enableSpy = t.spy(DragPanHandler.prototype, 'enable');
const customParams = {
linearity: 0.5,
easing: (t) => t,
maxSpeed: 1500,
deceleration: 1900
};
const map = createMap(t, null, customParams);

t.ok(enableSpy.calledWith(customParams));
t.deepEqual(map.dragPan._inertiaOptions, customParams);
t.end();
});

0 comments on commit db2f77c

Please sign in to comment.