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();
}
}
|