package.src.ui.handler_inertia.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mapbox-gl Show documentation
Show all versions of mapbox-gl Show documentation
A WebGL interactive maps library
The newest version!
// @flow
import browser from '../util/browser';
import type Map from './map';
import {bezier, clamp, extend} from '../util/util';
import Point from '@mapbox/point-geometry';
import type {DragPanOptions} from './handler/shim/drag_pan';
const defaultInertiaOptions = {
linearity: 0.3,
easing: bezier(0, 0, 0.3, 1),
};
const defaultPanInertiaOptions = extend({
deceleration: 2500,
maxSpeed: 1400
}, defaultInertiaOptions);
const defaultZoomInertiaOptions = extend({
deceleration: 20,
maxSpeed: 1400
}, defaultInertiaOptions);
const defaultBearingInertiaOptions = extend({
deceleration: 1000,
maxSpeed: 360
}, defaultInertiaOptions);
const defaultPitchInertiaOptions = extend({
deceleration: 1000,
maxSpeed: 90
}, defaultInertiaOptions);
export type InertiaOptions = {
linearity: number;
easing: (t: number) => number;
deceleration: number;
maxSpeed: number;
};
export type InputEvent = MouseEvent | TouchEvent | KeyboardEvent | WheelEvent;
export default class HandlerInertia {
_map: Map;
_inertiaBuffer: Array<{ time: number, settings: Object }>;
constructor(map: Map) {
this._map = map;
this.clear();
}
clear() {
this._inertiaBuffer = [];
}
record(settings: any) {
this._drainInertiaBuffer();
this._inertiaBuffer.push({time: browser.now(), settings});
}
_drainInertiaBuffer() {
const inertia = this._inertiaBuffer,
now = browser.now(),
cutoff = 160; //msec
while (inertia.length > 0 && now - inertia[0].time > cutoff)
inertia.shift();
}
_onMoveEnd(panInertiaOptions?: DragPanOptions) {
this._drainInertiaBuffer();
if (this._inertiaBuffer.length < 2) {
return;
}
const deltas = {
zoom: 0,
bearing: 0,
pitch: 0,
pan: new Point(0, 0),
pinchAround: undefined,
around: undefined
};
for (const {settings} of this._inertiaBuffer) {
deltas.zoom += settings.zoomDelta || 0;
deltas.bearing += settings.bearingDelta || 0;
deltas.pitch += settings.pitchDelta || 0;
if (settings.panDelta) deltas.pan._add(settings.panDelta);
if (settings.around) deltas.around = settings.around;
if (settings.pinchAround) deltas.pinchAround = settings.pinchAround;
}
const lastEntry = this._inertiaBuffer[this._inertiaBuffer.length - 1];
const duration = (lastEntry.time - this._inertiaBuffer[0].time);
const easeOptions = {};
if (deltas.pan.mag()) {
const result = calculateEasing(deltas.pan.mag(), duration, extend({}, defaultPanInertiaOptions, panInertiaOptions || {}));
easeOptions.offset = deltas.pan.mult(result.amount / deltas.pan.mag());
easeOptions.center = this._map.transform.center;
extendDuration(easeOptions, result);
}
if (deltas.zoom) {
const result = calculateEasing(deltas.zoom, duration, defaultZoomInertiaOptions);
easeOptions.zoom = this._map.transform.zoom + result.amount;
extendDuration(easeOptions, result);
}
if (deltas.bearing) {
const result = calculateEasing(deltas.bearing, duration, defaultBearingInertiaOptions);
easeOptions.bearing = this._map.transform.bearing + clamp(result.amount, -179, 179);
extendDuration(easeOptions, result);
}
if (deltas.pitch) {
const result = calculateEasing(deltas.pitch, duration, defaultPitchInertiaOptions);
easeOptions.pitch = this._map.transform.pitch + result.amount;
extendDuration(easeOptions, result);
}
if (easeOptions.zoom || easeOptions.bearing) {
const last = deltas.pinchAround === undefined ? deltas.around : deltas.pinchAround;
easeOptions.around = last ? this._map.unproject(last) : this._map.getCenter();
}
this.clear();
return extend(easeOptions, {
noMoveStart: true
});
}
}
// Unfortunately zoom, bearing, etc can't have different durations and easings so
// we need to choose one. We use the longest duration and it's corresponding easing.
function extendDuration(easeOptions, result) {
if (!easeOptions.duration || easeOptions.duration < result.duration) {
easeOptions.duration = result.duration;
easeOptions.easing = result.easing;
}
}
function calculateEasing(amount, inertiaDuration: number, inertiaOptions) {
const {maxSpeed, linearity, deceleration} = inertiaOptions;
const speed = clamp(
amount * linearity / (inertiaDuration / 1000),
-maxSpeed,
maxSpeed);
const duration = Math.abs(speed) / (deceleration * linearity);
return {
easing: inertiaOptions.easing,
duration: duration * 1000,
amount: speed * (duration / 2)
};
}