File size: 3,598 Bytes
11811dc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// @flow

import Point from '@mapbox/point-geometry';
import {indexTouches} from './handler_util';

function getCentroid(points: Array<Point>) {
    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<Point>, mapTouches: Array<Touch>) {

        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<Point>, mapTouches: Array<Touch>) {
        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<Point>, mapTouches: Array<Touch>) {
        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<Point>, mapTouches: Array<Touch>) {
        this.singleTap.touchstart(e, points, mapTouches);
    }

    touchmove(e: TouchEvent, points: Array<Point>, mapTouches: Array<Touch>) {
        this.singleTap.touchmove(e, points, mapTouches);
    }

    touchend(e: TouchEvent, points: Array<Point>, mapTouches: Array<Touch>) {
        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;
            }
        }
    }
}