-
Notifications
You must be signed in to change notification settings - Fork 190
/
Copy pathsuper-tabs-pan-gesture.ts
165 lines (115 loc) · 4.19 KB
/
super-tabs-pan-gesture.ts
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import { Platform } from 'ionic-angular';
import { pointerCoord, PointerCoordinates } from 'ionic-angular/util/dom';
import { SuperTabsConfig } from './components/super-tabs';
import { Renderer2 } from '@angular/core';
export class SuperTabsPanGesture {
onMove: (delta: number) => void;
onEnd: (shortSwipe: boolean, shortSwipeDelta?: number) => void;
private initialCoords: PointerCoordinates;
private initialTimestamp: number;
private leftThreshold: number = 0;
private rightThreshold: number = 0;
private shouldCapture: boolean;
private isDragging: boolean;
private lastPosX: number;
private listeners: Function[] = [];
constructor(
private plt: Platform,
private el: HTMLElement,
private config: SuperTabsConfig,
private rnd: Renderer2
) {
this.listeners.push(
rnd.listen(el, 'touchstart', this._onStart.bind(this)),
rnd.listen(el, 'touchmove', this._onMove.bind(this)),
rnd.listen(el, 'touchend', this._onEnd.bind(this))
);
if (config.sideMenu === 'both' || config.sideMenu === 'left') {
this.leftThreshold = config.sideMenuThreshold;
}
if (config.sideMenu === 'both' || config.sideMenu === 'right') {
this.rightThreshold = config.sideMenuThreshold;
}
}
destroy() {
this.listeners.forEach(fn => fn());
}
private _onStart(ev: TouchEvent) {
// check avoid this element
var avoid = false;
var element: any = ev.target;
if (element) {
do {
if (element.getAttribute && element.getAttribute('avoid-super-tabs')) avoid = true;
element = element.parentElement;
} while (element && !avoid);
}
if (avoid) {
this.shouldCapture = false;
return;
}
const coords: PointerCoordinates = pointerCoord(ev),
vw = this.plt.width();
if (coords.x < this.leftThreshold || coords.x > vw - this.rightThreshold) {
// ignore this gesture, it started in the side menu touch zone
this.shouldCapture = false;
return;
}
// the starting point looks good, let's see what happens when we move
this.initialCoords = coords;
if (this.config.shortSwipeDuration > 0) this.initialTimestamp = Date.now();
this.lastPosX = coords.x;
}
private _onMove(ev: TouchEvent) {
const coords: PointerCoordinates = pointerCoord(ev);
if (!this.isDragging) {
if (typeof this.shouldCapture !== 'boolean')
// we haven't decided yet if we want to capture this gesture
this.checkGesture(coords);
if (this.shouldCapture === true)
// gesture is good, let's capture all next onTouchMove events
this.isDragging = true;
else
return;
}
// stop anything else from capturing these events, to make sure the content doesn't slide
if (this.config.allowElementScroll !== true) {
ev.stopPropagation();
ev.preventDefault();
}
// get delta X
const deltaX: number = this.lastPosX - coords.x;
// emit value
this.onMove && this.onMove(deltaX);
// update last X value
this.lastPosX = coords.x;
}
private _onEnd(ev: TouchEvent) {
const coords: PointerCoordinates = pointerCoord(ev);
if (this.shouldCapture === true) {
if (this.config.shortSwipeDuration > 0) {
const deltaTime: number = Date.now() - this.initialTimestamp;
if (deltaTime <= this.config.shortSwipeDuration)
this.onEnd && this.onEnd(true, coords.x - this.initialCoords.x);
else this.onEnd && this.onEnd(false);
} else this.onEnd && this.onEnd(false);
}
this.isDragging = false;
this.shouldCapture = undefined;
}
private checkGesture(newCoords: PointerCoordinates) {
if (!this.initialCoords) return;
const radians = this.config.maxDragAngle * (Math.PI / 180),
maxCosine = Math.cos(radians),
deltaX = newCoords.x - this.initialCoords.x,
deltaY = newCoords.y - this.initialCoords.y,
distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance >= this.config.dragThreshold) {
// swipe is long enough so far
// lets check the angle
const angle = Math.atan2(deltaY, deltaX),
cosine = Math.cos(angle);
this.shouldCapture = Math.abs(cosine) > maxCosine;
}
}
}