package.src.ui.handler.tap_recognizer.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 Point from '@mapbox/point-geometry';
import {indexTouches} from './handler_util';
function getCentroid(points: Array) {
const sum = new Point(0, 0);
for (const point of points) {
sum._add(point);
}
return sum.div(points.length);
}
export const MAX_TAP_INTERVAL = 500;
const MAX_TOUCH_TIME = 500;
const MAX_DIST = 30;
export class SingleTapRecognizer {
numTouches: number;
centroid: Point;
startTime: number;
aborted: boolean;
touches: { [number | string]: Point };
constructor(options: { numTouches: number }) {
this.reset();
this.numTouches = options.numTouches;
}
reset() {
delete this.centroid;
delete this.startTime;
delete this.touches;
this.aborted = false;
}
touchstart(e: TouchEvent, points: Array, mapTouches: Array) {
if (this.centroid || mapTouches.length > this.numTouches) {
this.aborted = true;
}
if (this.aborted) {
return;
}
if (this.startTime === undefined) {
this.startTime = e.timeStamp;
}
if (mapTouches.length === this.numTouches) {
this.centroid = getCentroid(points);
this.touches = indexTouches(mapTouches, points);
}
}
touchmove(e: TouchEvent, points: Array, mapTouches: Array) {
if (this.aborted || !this.centroid) return;
const newTouches = indexTouches(mapTouches, points);
for (const id in this.touches) {
const prevPos = this.touches[id];
const pos = newTouches[id];
if (!pos || pos.dist(prevPos) > MAX_DIST) {
this.aborted = true;
}
}
}
touchend(e: TouchEvent, points: Array, mapTouches: Array) {
if (!this.centroid || e.timeStamp - this.startTime > MAX_TOUCH_TIME) {
this.aborted = true;
}
if (mapTouches.length === 0) {
const centroid = !this.aborted && this.centroid;
this.reset();
if (centroid) return centroid;
}
}
}
export class TapRecognizer {
singleTap: SingleTapRecognizer;
numTaps: number;
lastTime: number;
lastTap: Point;
count: number;
constructor(options: { numTaps: number, numTouches: number }) {
this.singleTap = new SingleTapRecognizer(options);
this.numTaps = options.numTaps;
this.reset();
}
reset() {
this.lastTime = Infinity;
delete this.lastTap;
this.count = 0;
this.singleTap.reset();
}
touchstart(e: TouchEvent, points: Array, mapTouches: Array) {
this.singleTap.touchstart(e, points, mapTouches);
}
touchmove(e: TouchEvent, points: Array, mapTouches: Array) {
this.singleTap.touchmove(e, points, mapTouches);
}
touchend(e: TouchEvent, points: Array, mapTouches: Array) {
const tap = this.singleTap.touchend(e, points, mapTouches);
if (tap) {
const soonEnough = e.timeStamp - this.lastTime < MAX_TAP_INTERVAL;
const closeEnough = !this.lastTap || this.lastTap.dist(tap) < MAX_DIST;
if (!soonEnough || !closeEnough) {
this.reset();
}
this.count++;
this.lastTime = e.timeStamp;
this.lastTap = tap;
if (this.count === this.numTaps) {
this.reset();
return tap;
}
}
}
}