aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/Touchable.tsx
blob: 251cd41e58fa1a01113f5dd6731e66121adb275a (plain)
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
135
136
137
138
139
140
import * as React from 'react';
import { action } from 'mobx';
import { InteractionUtils } from '../util/InteractionUtils';

const HOLD_DURATION = 1000;

export abstract class Touchable<T = {}> extends React.Component<T> {
    private holdTimer: NodeJS.Timeout | undefined;

    protected _touchDrag: boolean = false;
    protected prevPoints: Map<number, React.Touch> = new Map<number, React.Touch>();

    public FirstX: number = 0;
    public FirstY: number = 0;
    public SecondX: number = 0;
    public SecondY: number = 0;

    /**
     * When a touch even starts, we keep track of each touch that is associated with that event
     */
    @action
    protected onTouchStart = (e: React.TouchEvent): void => {
        for (let i = 0; i < e.targetTouches.length; i++) {
            const pt: any = e.targetTouches.item(i);
            // pen is also a touch, but with a radius of 0.5 (at least with the surface pens)
            // and this seems to be the only way of differentiating pen and touch on touch events
            if (pt.radiusX > 0.5 && pt.radiusY > 0.5) {
                this.prevPoints.set(pt.identifier, pt);
            }
        }

        if (this.prevPoints.size) {
            switch (this.prevPoints.size) {
                case 1:
                    this.handle1PointerDown(e);
                    e.persist();
                    this.holdTimer = setTimeout(() => this.handle1PointerHoldStart(e), HOLD_DURATION);
                    break;
                case 2:
                    this.handle2PointersDown(e);
                    break;
            }
        }
    }

    /**
    * Handle touch move event
    */
    @action
    protected onTouch = (e: TouchEvent): void => {
        const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints);

        // if we're not actually moving a lot, don't consider it as dragging yet
        if (!InteractionUtils.IsDragging(this.prevPoints, myTouches, 5) && !this._touchDrag) return;
        this._touchDrag = true;
        if (this.holdTimer) {
            clearTimeout(this.holdTimer);
        }
        switch (myTouches.length) {
            case 1:
                this.handle1PointerMove(e);
                break;
            case 2:
                this.handle2PointersMove(e);
                break;
        }

        for (let i = 0; i < e.targetTouches.length; i++) {
            const pt = e.targetTouches.item(i);
            if (pt) {
                if (this.prevPoints.has(pt.identifier)) {
                    this.prevPoints.set(pt.identifier, pt);
                }
            }
        }
    }

    @action
    protected onTouchEnd = (e: TouchEvent): void => {
        // console.log(InteractionUtils.GetMyTargetTouches(e, this.prevPoints).length + " up");
        // remove all the touches associated with the event
        for (let i = 0; i < e.changedTouches.length; i++) {
            const pt = e.changedTouches.item(i);
            if (pt) {
                if (this.prevPoints.has(pt.identifier)) {
                    this.prevPoints.delete(pt.identifier);
                }
            }
        }
        if (this.holdTimer) {
            clearTimeout(this.holdTimer);
        }
        this._touchDrag = false;
        e.stopPropagation();


        // if (e.targetTouches.length === 0) {
        //     this.prevPoints.clear();
        // }

        if (this.prevPoints.size === 0) {
            this.cleanUpInteractions();
        }
    }

    cleanUpInteractions = (): void => {
        document.removeEventListener("touchmove", this.onTouch);
        document.removeEventListener("touchend", this.onTouchEnd);
    }

    handle1PointerMove = (e: TouchEvent): any => {
        e.stopPropagation();
        e.preventDefault();
    }

    handle2PointersMove = (e: TouchEvent): any => {
        e.stopPropagation();
        e.preventDefault();
    }

    handle1PointerDown = (e: React.TouchEvent): any => {
        document.removeEventListener("touchmove", this.onTouch);
        document.addEventListener("touchmove", this.onTouch);
        document.removeEventListener("touchend", this.onTouchEnd);
        document.addEventListener("touchend", this.onTouchEnd);
    }

    handle2PointersDown = (e: React.TouchEvent): any => {
        document.removeEventListener("touchmove", this.onTouch);
        document.addEventListener("touchmove", this.onTouch);
        document.removeEventListener("touchend", this.onTouchEnd);
        document.addEventListener("touchend", this.onTouchEnd);
    }

    handle1PointerHoldStart = (e: React.TouchEvent): any => {
        console.log("Hold");
        e.stopPropagation();
        e.preventDefault();
    }
}