react-native-gesture-handler 1.2.1 → 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
- package/DrawerLayout.js +5 -4
- package/GestureButtons.js +166 -0
- package/GestureComponents.js +63 -0
- package/GestureComponents.web.js +35 -0
- package/GestureHandler.js +10 -621
- package/GestureHandlerButton.web.js +4 -12
- package/GestureHandlerPropTypes.js +45 -0
- package/Gestures.js +278 -0
- package/NativeViewGestureHandler.js +14 -0
- package/PlatformConstants.web.js +3 -1
- package/RNGestureHandler.podspec +1 -1
- package/RNGestureHandlerModule.web.js +49 -0
- package/State.js +12 -1
- package/Swipeable.js +6 -11
- package/android/build.gradle +3 -7
- package/android/lib/src/main/java/com/swmansion/gesturehandler/GestureHandlerOrchestrator.java +1 -1
- package/android/lib/src/main/java/com/swmansion/gesturehandler/PanGestureHandler.java +1 -1
- package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerEnabledRootView.java +1 -1
- package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerEvent.java +2 -2
- package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.java +1 -1
- package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerPackage.java +1 -1
- package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRegistry.java +1 -1
- package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootInterface.java +1 -1
- package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootView.java +1 -1
- package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootViewManager.java +1 -1
- package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerStateChangeEvent.java +2 -2
- package/createHandler.js +46 -20
- package/createNativeWrapper.js +86 -0
- package/ios/RNGestureHandler.xcodeproj/project.pbxproj +4 -4
- package/package.json +20 -17
- package/react-native-gesture-handler.d.ts +25 -3
- package/touchables/GenericTouchable.js +3 -1
- package/touchables/TouchableHighlight.js +1 -3
- package/touchables/TouchableOpacity.web.js +2 -0
- package/touchables/TouchableWithoutFeedback.js +4 -2
- package/web/DiscreteGestureHandler.js +66 -0
- package/web/DraggingGestureHandler.js +22 -0
- package/web/Errors.js +5 -0
- package/web/FlingGestureHandler.js +137 -0
- package/web/GestureHandler.js +442 -0
- package/web/IndiscreteGestureHandler.js +33 -0
- package/web/LongPressGestureHandler.js +50 -0
- package/web/NativeViewGestureHandler.js +38 -0
- package/web/NodeManager.js +24 -0
- package/web/PanGestureHandler.js +213 -0
- package/web/PinchGestureHandler.js +24 -0
- package/web/PressGestureHandler.js +147 -0
- package/web/RotationGestureHandler.js +24 -0
- package/web/TapGestureHandler.js +160 -0
- package/web/constants.js +48 -0
- package/web/utils.js +14 -0
- package/Directions.web.js +0 -6
- package/Swipeable.web.js +0 -4
- package/createHandler.web.js +0 -205
@@ -0,0 +1,213 @@
|
|
1
|
+
import Hammer from 'hammerjs';
|
2
|
+
|
3
|
+
import {
|
4
|
+
MULTI_FINGER_PAN_MAX_PINCH_THRESHOLD,
|
5
|
+
MULTI_FINGER_PAN_MAX_ROTATION_THRESHOLD,
|
6
|
+
} from './constants';
|
7
|
+
import DraggingGestureHandler from './DraggingGestureHandler';
|
8
|
+
import { isnan, TEST_MIN_IF_NOT_NAN, VEC_LEN_SQ } from './utils';
|
9
|
+
|
10
|
+
class PanGestureHandler extends DraggingGestureHandler {
|
11
|
+
get name() {
|
12
|
+
return 'pan';
|
13
|
+
}
|
14
|
+
|
15
|
+
get NativeGestureClass() {
|
16
|
+
return Hammer.Pan;
|
17
|
+
}
|
18
|
+
|
19
|
+
getHammerConfig() {
|
20
|
+
return {
|
21
|
+
...super.getHammerConfig(),
|
22
|
+
direction: this.getDirection(),
|
23
|
+
};
|
24
|
+
}
|
25
|
+
|
26
|
+
getDirection() {
|
27
|
+
const config = this.getConfig();
|
28
|
+
const {
|
29
|
+
activeOffsetXStart,
|
30
|
+
activeOffsetXEnd,
|
31
|
+
activeOffsetYStart,
|
32
|
+
activeOffsetYEnd,
|
33
|
+
minDist,
|
34
|
+
} = config;
|
35
|
+
let directions = [];
|
36
|
+
let horizontalDirections = [];
|
37
|
+
|
38
|
+
if (!isnan(minDist)) {
|
39
|
+
return Hammer.DIRECTION_ALL;
|
40
|
+
}
|
41
|
+
|
42
|
+
if (!isnan(activeOffsetXStart)) horizontalDirections.push(Hammer.DIRECTION_LEFT);
|
43
|
+
if (!isnan(activeOffsetXEnd)) horizontalDirections.push(Hammer.DIRECTION_RIGHT);
|
44
|
+
if (horizontalDirections.length === 2) horizontalDirections = [Hammer.DIRECTION_HORIZONTAL];
|
45
|
+
|
46
|
+
directions = directions.concat(horizontalDirections);
|
47
|
+
let verticalDirections = [];
|
48
|
+
|
49
|
+
if (!isnan(activeOffsetYStart)) verticalDirections.push(Hammer.DIRECTION_UP);
|
50
|
+
if (!isnan(activeOffsetYEnd)) verticalDirections.push(Hammer.DIRECTION_DOWN);
|
51
|
+
|
52
|
+
if (verticalDirections.length === 2) verticalDirections = [Hammer.DIRECTION_VERTICAL];
|
53
|
+
|
54
|
+
directions = directions.concat(verticalDirections);
|
55
|
+
|
56
|
+
if (!directions.length) {
|
57
|
+
return Hammer.DIRECTION_NONE;
|
58
|
+
}
|
59
|
+
if (
|
60
|
+
directions[0] === Hammer.DIRECTION_HORIZONTAL &&
|
61
|
+
directions[1] === Hammer.DIRECTION_VERTICAL
|
62
|
+
) {
|
63
|
+
return Hammer.DIRECTION_ALL;
|
64
|
+
}
|
65
|
+
if (horizontalDirections.length && verticalDirections.length) {
|
66
|
+
return Hammer.DIRECTION_ALL;
|
67
|
+
}
|
68
|
+
|
69
|
+
return directions[0];
|
70
|
+
}
|
71
|
+
|
72
|
+
getConfig() {
|
73
|
+
if (!this._hasCustomActivationCriteria) {
|
74
|
+
// Default config
|
75
|
+
// If no params have been defined then this config should emulate the native gesture as closely as possible.
|
76
|
+
return {
|
77
|
+
minDistSq: 10,
|
78
|
+
};
|
79
|
+
}
|
80
|
+
return this.config;
|
81
|
+
}
|
82
|
+
|
83
|
+
shouldFailUnderCustomCriteria({ deltaX, deltaY }, criteria) {
|
84
|
+
return (
|
85
|
+
(!isnan(criteria.failOffsetXStart) && deltaX < criteria.failOffsetXStart) ||
|
86
|
+
(!isnan(criteria.failOffsetXEnd) && deltaX > criteria.failOffsetXEnd) ||
|
87
|
+
(!isnan(criteria.failOffsetYStart) && deltaY < criteria.failOffsetYStart) ||
|
88
|
+
(!isnan(criteria.failOffsetYEnd) && deltaY > criteria.failOffsetYEnd)
|
89
|
+
);
|
90
|
+
}
|
91
|
+
|
92
|
+
shouldActivateUnderCustomCriteria({ deltaX, deltaY, velocity }, criteria) {
|
93
|
+
return (
|
94
|
+
(!isnan(criteria.activeOffsetXStart) && deltaX < criteria.activeOffsetXStart) ||
|
95
|
+
(!isnan(criteria.activeOffsetXEnd) && deltaX > criteria.activeOffsetXEnd) ||
|
96
|
+
(!isnan(criteria.activeOffsetYStart) && deltaY < criteria.activeOffsetYStart) ||
|
97
|
+
(!isnan(criteria.activeOffsetYEnd) && deltaY > criteria.activeOffsetYEnd) ||
|
98
|
+
TEST_MIN_IF_NOT_NAN(VEC_LEN_SQ({ x: deltaX, y: deltaY }), criteria.minDistSq) ||
|
99
|
+
TEST_MIN_IF_NOT_NAN(velocity.x, criteria.minVelocityX) ||
|
100
|
+
TEST_MIN_IF_NOT_NAN(velocity.y, criteria.minVelocityY) ||
|
101
|
+
TEST_MIN_IF_NOT_NAN(VEC_LEN_SQ(velocity), criteria.minVelocitySq)
|
102
|
+
);
|
103
|
+
}
|
104
|
+
|
105
|
+
shouldMultiFingerPanFail({ pointerLength, scale, deltaRotation }) {
|
106
|
+
if (pointerLength <= 1) {
|
107
|
+
return false;
|
108
|
+
}
|
109
|
+
|
110
|
+
// Test if the pan had too much pinching or rotating.
|
111
|
+
const deltaScale = Math.abs(scale - 1);
|
112
|
+
const absDeltaRotation = Math.abs(deltaRotation);
|
113
|
+
if (deltaScale > MULTI_FINGER_PAN_MAX_PINCH_THRESHOLD) {
|
114
|
+
// > If the threshold doesn't seem right.
|
115
|
+
// You can log the value which it failed at here:
|
116
|
+
return true;
|
117
|
+
}
|
118
|
+
if (absDeltaRotation > MULTI_FINGER_PAN_MAX_ROTATION_THRESHOLD) {
|
119
|
+
// > If the threshold doesn't seem right.
|
120
|
+
// You can log the value which it failed at here:
|
121
|
+
return true;
|
122
|
+
}
|
123
|
+
|
124
|
+
return false;
|
125
|
+
}
|
126
|
+
|
127
|
+
updateHasCustomActivationCriteria(criteria) {
|
128
|
+
return (
|
129
|
+
!isnan(criteria.minDistSq) ||
|
130
|
+
!isnan(criteria.minVelocityX) ||
|
131
|
+
!isnan(criteria.minVelocityY) ||
|
132
|
+
!isnan(criteria.minVelocitySq) ||
|
133
|
+
!isnan(criteria.activeOffsetXStart) ||
|
134
|
+
!isnan(criteria.activeOffsetXEnd) ||
|
135
|
+
!isnan(criteria.activeOffsetYStart) ||
|
136
|
+
!isnan(criteria.activeOffsetYEnd)
|
137
|
+
);
|
138
|
+
}
|
139
|
+
|
140
|
+
isGestureEnabledForEvent(props, recognizer, inputData) {
|
141
|
+
if (this.shouldFailUnderCustomCriteria(inputData, props)) {
|
142
|
+
return { failed: true };
|
143
|
+
}
|
144
|
+
|
145
|
+
const velocity = { x: inputData.velocityX, y: inputData.velocityY };
|
146
|
+
if (
|
147
|
+
this._hasCustomActivationCriteria &&
|
148
|
+
this.shouldActivateUnderCustomCriteria(
|
149
|
+
{ deltaX: inputData.deltaX, deltaY: inputData.deltaY, velocity },
|
150
|
+
props
|
151
|
+
)
|
152
|
+
) {
|
153
|
+
if (
|
154
|
+
this.shouldMultiFingerPanFail({
|
155
|
+
pointerLength: inputData.maxPointers,
|
156
|
+
scale: inputData.scale,
|
157
|
+
deltaRotation: inputData.deltaRotation,
|
158
|
+
})
|
159
|
+
) {
|
160
|
+
return {
|
161
|
+
failed: true,
|
162
|
+
};
|
163
|
+
}
|
164
|
+
return { success: true };
|
165
|
+
}
|
166
|
+
return { success: false };
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
170
|
+
function validateConfig(config = {}) {
|
171
|
+
const isNum = v => isnan(v) || typeof v === 'number';
|
172
|
+
const isBool = v => typeof v === 'boolean';
|
173
|
+
|
174
|
+
const valid = {
|
175
|
+
enabled: isBool,
|
176
|
+
minDistSq: isNum,
|
177
|
+
minVelocityX: isNum,
|
178
|
+
minVelocityY: isNum,
|
179
|
+
// TODO: Bacon: remove `minVelocity`
|
180
|
+
minVelocity: isNum,
|
181
|
+
minVelocitySq: isNum,
|
182
|
+
activeOffsetXStart: isNum,
|
183
|
+
activeOffsetXEnd: isNum,
|
184
|
+
failOffsetXStart: isNum,
|
185
|
+
failOffsetXEnd: isNum,
|
186
|
+
activeOffsetYStart: isNum,
|
187
|
+
activeOffsetYEnd: isNum,
|
188
|
+
failOffsetYStart: isNum,
|
189
|
+
failOffsetYEnd: isNum,
|
190
|
+
hasCustomActivationCriteria: isBool,
|
191
|
+
minPointers: isNum,
|
192
|
+
maxPointers: isNum,
|
193
|
+
};
|
194
|
+
const keys = Object.keys(valid);
|
195
|
+
|
196
|
+
let invalidKeys = [];
|
197
|
+
for (const key of Object.keys(config)) {
|
198
|
+
if (keys.includes(key)) {
|
199
|
+
if (valid[key](config[key])) {
|
200
|
+
console.warn('Invalid type: ' + key + ': ' + config[key]);
|
201
|
+
}
|
202
|
+
} else {
|
203
|
+
invalidKeys.push(key);
|
204
|
+
}
|
205
|
+
}
|
206
|
+
|
207
|
+
if (invalidKeys.length) {
|
208
|
+
throw new Error('Invalid config props found: ' + invalidKeys.join(', '));
|
209
|
+
}
|
210
|
+
return config;
|
211
|
+
}
|
212
|
+
|
213
|
+
export default PanGestureHandler;
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import Hammer from 'hammerjs';
|
2
|
+
|
3
|
+
import IndiscreteGestureHandler from './IndiscreteGestureHandler';
|
4
|
+
|
5
|
+
class PinchGestureHandler extends IndiscreteGestureHandler {
|
6
|
+
get name() {
|
7
|
+
return 'pinch';
|
8
|
+
}
|
9
|
+
|
10
|
+
get NativeGestureClass() {
|
11
|
+
return Hammer.Pinch;
|
12
|
+
}
|
13
|
+
|
14
|
+
transformNativeEvent({ scale, velocity, center }) {
|
15
|
+
return {
|
16
|
+
focalX: center.x,
|
17
|
+
focalY: center.y,
|
18
|
+
velocity,
|
19
|
+
scale,
|
20
|
+
};
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
export default PinchGestureHandler;
|
@@ -0,0 +1,147 @@
|
|
1
|
+
import Hammer from 'hammerjs';
|
2
|
+
|
3
|
+
import State from '../State';
|
4
|
+
import { CONTENT_TOUCHES_DELAY, CONTENT_TOUCHES_QUICK_TAP_END_DELAY } from './constants';
|
5
|
+
import DiscreteGestureHandler from './DiscreteGestureHandler';
|
6
|
+
import { fireAfterInterval, isnan } from './utils';
|
7
|
+
|
8
|
+
class PressGestureHandler extends DiscreteGestureHandler {
|
9
|
+
get name() {
|
10
|
+
return 'press';
|
11
|
+
}
|
12
|
+
|
13
|
+
get minDurationMs() {
|
14
|
+
return isnan(this.config.minDurationMs) ? 5 : this.config.minDurationMs;
|
15
|
+
}
|
16
|
+
|
17
|
+
get maxDist() {
|
18
|
+
return isnan(this.config.maxDist) ? 9 : this.config.maxDist;
|
19
|
+
}
|
20
|
+
|
21
|
+
get NativeGestureClass() {
|
22
|
+
return Hammer.Press;
|
23
|
+
}
|
24
|
+
|
25
|
+
shouldDelayTouches = true;
|
26
|
+
|
27
|
+
simulateCancelEvent(inputData) {
|
28
|
+
// Long press never starts so we can't rely on the running event boolean.
|
29
|
+
this.hasGestureFailed = true;
|
30
|
+
this.cancelEvent(inputData);
|
31
|
+
}
|
32
|
+
|
33
|
+
updateHasCustomActivationCriteria({ shouldCancelWhenOutside, maxDistSq }) {
|
34
|
+
return shouldCancelWhenOutside || !isnan(maxDistSq);
|
35
|
+
}
|
36
|
+
|
37
|
+
getState(type) {
|
38
|
+
return {
|
39
|
+
[Hammer.INPUT_START]: State.BEGAN,
|
40
|
+
[Hammer.INPUT_MOVE]: State.ACTIVE,
|
41
|
+
[Hammer.INPUT_END]: State.END,
|
42
|
+
[Hammer.INPUT_CANCEL]: State.CANCELLED,
|
43
|
+
}[type];
|
44
|
+
}
|
45
|
+
|
46
|
+
getConfig() {
|
47
|
+
if (!this._hasCustomActivationCriteria) {
|
48
|
+
// Default config
|
49
|
+
// If no params have been defined then this config should emulate the native gesture as closely as possible.
|
50
|
+
return {
|
51
|
+
shouldCancelWhenOutside: true,
|
52
|
+
maxDistSq: 10,
|
53
|
+
};
|
54
|
+
}
|
55
|
+
return this.config;
|
56
|
+
}
|
57
|
+
|
58
|
+
getHammerConfig() {
|
59
|
+
return {
|
60
|
+
...super.getHammerConfig(),
|
61
|
+
// threshold: this.maxDist,
|
62
|
+
time: this.minDurationMs,
|
63
|
+
};
|
64
|
+
}
|
65
|
+
|
66
|
+
onGestureActivated(ev) {
|
67
|
+
this.onGestureStart(ev);
|
68
|
+
}
|
69
|
+
|
70
|
+
shouldDelayTouchForEvent({ pointerType }) {
|
71
|
+
// Don't disable event for mouse input
|
72
|
+
return this.shouldDelayTouches && pointerType === 'touch';
|
73
|
+
}
|
74
|
+
|
75
|
+
onGestureStart(ev) {
|
76
|
+
this.isGestureRunning = true;
|
77
|
+
clearTimeout(this.visualFeedbackTimer);
|
78
|
+
this.initialEvent = ev;
|
79
|
+
this.visualFeedbackTimer = fireAfterInterval(() => {
|
80
|
+
this.sendGestureStartedEvent(this.initialEvent);
|
81
|
+
this.initialEvent = null;
|
82
|
+
}, this.shouldDelayTouchForEvent(ev) && CONTENT_TOUCHES_DELAY);
|
83
|
+
}
|
84
|
+
|
85
|
+
sendGestureStartedEvent(ev) {
|
86
|
+
clearTimeout(this.visualFeedbackTimer);
|
87
|
+
this.visualFeedbackTimer = null;
|
88
|
+
this.sendEvent({
|
89
|
+
...ev,
|
90
|
+
eventType: Hammer.INPUT_MOVE,
|
91
|
+
isFirst: true,
|
92
|
+
});
|
93
|
+
}
|
94
|
+
|
95
|
+
forceInvalidate(event) {
|
96
|
+
super.forceInvalidate(event);
|
97
|
+
clearTimeout(this.visualFeedbackTimer);
|
98
|
+
this.visualFeedbackTimer = null;
|
99
|
+
this.initialEvent = null;
|
100
|
+
}
|
101
|
+
|
102
|
+
onRawEvent(ev) {
|
103
|
+
super.onRawEvent(ev);
|
104
|
+
if (ev.isFinal && this.isGestureRunning) {
|
105
|
+
let timeout;
|
106
|
+
if (this.visualFeedbackTimer) {
|
107
|
+
// Aesthetic timing for a quick tap.
|
108
|
+
// We haven't activated the tap right away to emulate iOS `delaysContentTouches`
|
109
|
+
// Now we must send the initial activation event and wait a set amount of time before firing the end event.
|
110
|
+
timeout = CONTENT_TOUCHES_QUICK_TAP_END_DELAY;
|
111
|
+
this.sendGestureStartedEvent(this.initialEvent);
|
112
|
+
this.initialEvent = null;
|
113
|
+
}
|
114
|
+
fireAfterInterval(() => {
|
115
|
+
this.sendEvent({
|
116
|
+
...ev,
|
117
|
+
eventType: Hammer.INPUT_END,
|
118
|
+
isFinal: true,
|
119
|
+
});
|
120
|
+
this.onGestureEnded();
|
121
|
+
}, timeout);
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
updateGestureConfig({
|
126
|
+
shouldActivateOnStart = false,
|
127
|
+
disallowInterruption = false,
|
128
|
+
shouldCancelWhenOutside = true,
|
129
|
+
minDurationMs = Number.NaN,
|
130
|
+
maxDist = Number.NaN,
|
131
|
+
minPointers = 1,
|
132
|
+
maxPointers = 1,
|
133
|
+
...props
|
134
|
+
}) {
|
135
|
+
return super.updateGestureConfig({
|
136
|
+
shouldActivateOnStart,
|
137
|
+
disallowInterruption,
|
138
|
+
shouldCancelWhenOutside,
|
139
|
+
minDurationMs,
|
140
|
+
maxDist,
|
141
|
+
minPointers,
|
142
|
+
maxPointers,
|
143
|
+
...props,
|
144
|
+
});
|
145
|
+
}
|
146
|
+
}
|
147
|
+
export default PressGestureHandler;
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import Hammer from 'hammerjs';
|
2
|
+
|
3
|
+
import { DEG_RAD } from './constants';
|
4
|
+
import IndiscreteGestureHandler from './IndiscreteGestureHandler';
|
5
|
+
|
6
|
+
class RotationGestureHandler extends IndiscreteGestureHandler {
|
7
|
+
get name() {
|
8
|
+
return 'rotate';
|
9
|
+
}
|
10
|
+
|
11
|
+
get NativeGestureClass() {
|
12
|
+
return Hammer.Rotate;
|
13
|
+
}
|
14
|
+
|
15
|
+
transformNativeEvent({ rotation, velocity, center }) {
|
16
|
+
return {
|
17
|
+
rotation: (rotation - this.initialRotation) * DEG_RAD,
|
18
|
+
anchorX: center.x,
|
19
|
+
anchorY: center.y,
|
20
|
+
velocity,
|
21
|
+
};
|
22
|
+
}
|
23
|
+
}
|
24
|
+
export default RotationGestureHandler;
|
@@ -0,0 +1,160 @@
|
|
1
|
+
import Hammer from 'hammerjs';
|
2
|
+
|
3
|
+
import DiscreteGestureHandler from './DiscreteGestureHandler';
|
4
|
+
import { isnan } from './utils';
|
5
|
+
|
6
|
+
class TapGestureHandler extends DiscreteGestureHandler {
|
7
|
+
get name() {
|
8
|
+
return 'tap';
|
9
|
+
}
|
10
|
+
|
11
|
+
get NativeGestureClass() {
|
12
|
+
return Hammer.Tap;
|
13
|
+
}
|
14
|
+
|
15
|
+
get maxDelayMs() {
|
16
|
+
return isnan(this.config.maxDelayMs) ? 300 : this.config.maxDelayMs;
|
17
|
+
}
|
18
|
+
|
19
|
+
simulateCancelEvent(inputData) {
|
20
|
+
if (this.isGestureRunning) {
|
21
|
+
this.cancelEvent(inputData);
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
onGestureActivated(ev) {
|
26
|
+
if (this.isGestureRunning) {
|
27
|
+
this.onSuccessfulTap(ev);
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
onSuccessfulTap = ev => {
|
32
|
+
if (this._getPendingGestures().length) {
|
33
|
+
this._shouldFireEndEvent = ev;
|
34
|
+
return;
|
35
|
+
}
|
36
|
+
if (ev.eventType === Hammer.INPUT_END) {
|
37
|
+
this.sendEvent({ ...ev, eventType: Hammer.INPUT_MOVE });
|
38
|
+
}
|
39
|
+
// When handler gets activated it will turn into State.END immediately.
|
40
|
+
this.sendEvent({ ...ev, isFinal: true });
|
41
|
+
this.onGestureEnded(ev);
|
42
|
+
};
|
43
|
+
|
44
|
+
onRawEvent(ev) {
|
45
|
+
super.onRawEvent(ev);
|
46
|
+
|
47
|
+
// Attempt to create a touch-down event by checking if a valid tap hasn't started yet, then validating the input.
|
48
|
+
if (
|
49
|
+
!this.hasGestureFailed &&
|
50
|
+
!this.isGestureRunning &&
|
51
|
+
// Prevent multi-pointer events from misfiring.
|
52
|
+
!ev.isFinal
|
53
|
+
) {
|
54
|
+
// Tap Gesture start event
|
55
|
+
const gesture = this.hammer.get(this.name);
|
56
|
+
if (gesture.options.enable(gesture, ev)) {
|
57
|
+
clearTimeout(this._multiTapTimer);
|
58
|
+
|
59
|
+
this.onStart(ev);
|
60
|
+
this.sendEvent(ev);
|
61
|
+
}
|
62
|
+
}
|
63
|
+
if (ev.isFinal && ev.maxPointers > 1) {
|
64
|
+
setTimeout(() => {
|
65
|
+
// Handle case where one finger presses slightly
|
66
|
+
// after the first finger on a multi-tap event
|
67
|
+
if (this.isGestureRunning) {
|
68
|
+
this.cancelEvent(ev);
|
69
|
+
}
|
70
|
+
});
|
71
|
+
}
|
72
|
+
|
73
|
+
if (this.hasGestureFailed) {
|
74
|
+
return;
|
75
|
+
}
|
76
|
+
// Hammer doesn't send a `cancel` event for taps.
|
77
|
+
// Manually fail the event.
|
78
|
+
if (ev.isFinal) {
|
79
|
+
// Handle case where one finger presses slightly
|
80
|
+
// after the first finger on a multi-tap event
|
81
|
+
if (ev.maxPointers > 1) {
|
82
|
+
setTimeout(() => {
|
83
|
+
if (this.isGestureRunning) {
|
84
|
+
this.cancelEvent(ev);
|
85
|
+
}
|
86
|
+
});
|
87
|
+
}
|
88
|
+
|
89
|
+
// Clear last timer
|
90
|
+
clearTimeout(this._timer);
|
91
|
+
// Create time out for multi-taps.
|
92
|
+
this._timer = setTimeout(() => {
|
93
|
+
this.hasGestureFailed = true;
|
94
|
+
this.cancelEvent(ev);
|
95
|
+
}, this.maxDelayMs);
|
96
|
+
} else if (!this.hasGestureFailed && !this.isGestureRunning) {
|
97
|
+
// Tap Gesture start event
|
98
|
+
const gesture = this.hammer.get(this.name);
|
99
|
+
if (gesture.options.enable(gesture, ev)) {
|
100
|
+
clearTimeout(this._multiTapTimer);
|
101
|
+
|
102
|
+
this.onStart(ev);
|
103
|
+
this.sendEvent(ev);
|
104
|
+
}
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
getHammerConfig() {
|
109
|
+
return {
|
110
|
+
...super.getHammerConfig(),
|
111
|
+
event: this.name,
|
112
|
+
taps: isnan(this.config.numberOfTaps) ? 1 : this.config.numberOfTaps,
|
113
|
+
interval: this.maxDelayMs,
|
114
|
+
time:
|
115
|
+
isnan(this.config.maxDurationMs) || this.config.maxDurationMs == null
|
116
|
+
? 250
|
117
|
+
: this.config.maxDurationMs,
|
118
|
+
};
|
119
|
+
}
|
120
|
+
|
121
|
+
updateGestureConfig({
|
122
|
+
shouldCancelWhenOutside = true,
|
123
|
+
maxDeltaX = Number.NaN,
|
124
|
+
maxDeltaY = Number.NaN,
|
125
|
+
numberOfTaps = 1,
|
126
|
+
minDurationMs = 525,
|
127
|
+
maxDelayMs = Number.NaN,
|
128
|
+
maxDurationMs = Number.NaN,
|
129
|
+
maxDist = 2,
|
130
|
+
minPointers = 1,
|
131
|
+
maxPointers = 1,
|
132
|
+
...props
|
133
|
+
}) {
|
134
|
+
return super.updateGestureConfig({
|
135
|
+
shouldCancelWhenOutside,
|
136
|
+
numberOfTaps,
|
137
|
+
maxDeltaX,
|
138
|
+
maxDeltaY,
|
139
|
+
minDurationMs,
|
140
|
+
maxDelayMs,
|
141
|
+
maxDist,
|
142
|
+
minPointers,
|
143
|
+
maxPointers,
|
144
|
+
...props,
|
145
|
+
});
|
146
|
+
}
|
147
|
+
|
148
|
+
onGestureEnded(...props) {
|
149
|
+
clearTimeout(this._timer);
|
150
|
+
super.onGestureEnded(...props);
|
151
|
+
}
|
152
|
+
|
153
|
+
onWaitingEnded(gesture) {
|
154
|
+
if (this._shouldFireEndEvent) {
|
155
|
+
this.onSuccessfulTap(this._shouldFireEndEvent);
|
156
|
+
this._shouldFireEndEvent = null;
|
157
|
+
}
|
158
|
+
}
|
159
|
+
}
|
160
|
+
export default TapGestureHandler;
|
package/web/constants.js
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
import Hammer from 'hammerjs';
|
2
|
+
|
3
|
+
import State from '../State';
|
4
|
+
|
5
|
+
export const CONTENT_TOUCHES_DELAY = 240;
|
6
|
+
export const CONTENT_TOUCHES_QUICK_TAP_END_DELAY = 50;
|
7
|
+
export const MULTI_FINGER_PAN_MAX_PINCH_THRESHOLD = 0.1;
|
8
|
+
export const MULTI_FINGER_PAN_MAX_ROTATION_THRESHOLD = 7;
|
9
|
+
export const DEG_RAD = Math.PI / 180;
|
10
|
+
|
11
|
+
// Map Hammer values to RNGH
|
12
|
+
export const EventMap = {
|
13
|
+
[Hammer.INPUT_START]: State.BEGAN,
|
14
|
+
[Hammer.INPUT_MOVE]: State.ACTIVE,
|
15
|
+
[Hammer.INPUT_END]: State.END,
|
16
|
+
[Hammer.INPUT_CANCEL]: State.FAILED,
|
17
|
+
};
|
18
|
+
|
19
|
+
export const Direction = {
|
20
|
+
RIGHT: 1,
|
21
|
+
LEFT: 2,
|
22
|
+
UP: 4,
|
23
|
+
DOWN: 8,
|
24
|
+
};
|
25
|
+
|
26
|
+
export const DirectionMap = {
|
27
|
+
[Hammer.DIRECTION_RIGHT]: Direction.RIGHT,
|
28
|
+
[Hammer.DIRECTION_LEFT]: Direction.LEFT,
|
29
|
+
[Hammer.DIRECTION_UP]: Direction.UP,
|
30
|
+
[Hammer.DIRECTION_DOWN]: Direction.DOWN,
|
31
|
+
};
|
32
|
+
|
33
|
+
export const HammerInputNames = {
|
34
|
+
[Hammer.INPUT_START]: 'START',
|
35
|
+
[Hammer.INPUT_MOVE]: 'MOVE',
|
36
|
+
[Hammer.INPUT_END]: 'END',
|
37
|
+
[Hammer.INPUT_CANCEL]: 'CANCEL',
|
38
|
+
};
|
39
|
+
export const HammerDirectionNames = {
|
40
|
+
[Hammer.DIRECTION_HORIZONTAL]: 'HORIZONTAL',
|
41
|
+
[Hammer.DIRECTION_UP]: 'UP',
|
42
|
+
[Hammer.DIRECTION_DOWN]: 'DOWN',
|
43
|
+
[Hammer.DIRECTION_VERTICAL]: 'VERTICAL',
|
44
|
+
[Hammer.DIRECTION_NONE]: 'NONE',
|
45
|
+
[Hammer.DIRECTION_ALL]: 'ALL',
|
46
|
+
[Hammer.DIRECTION_RIGHT]: 'RIGHT',
|
47
|
+
[Hammer.DIRECTION_LEFT]: 'LEFT',
|
48
|
+
};
|
package/web/utils.js
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
export const isnan = v => Number.isNaN(v);
|
2
|
+
export const TEST_MIN_IF_NOT_NAN = (value, limit) =>
|
3
|
+
!isnan(limit) && ((limit < 0 && value <= limit) || (limit >= 0 && value >= limit));
|
4
|
+
export const VEC_LEN_SQ = ({ x = 0, y = 0 } = {}) => x * x + y * y;
|
5
|
+
export const TEST_MAX_IF_NOT_NAN = (value, max) =>
|
6
|
+
!isnan(max) && ((max < 0 && value < max) || (max >= 0 && value > max));
|
7
|
+
|
8
|
+
export function fireAfterInterval(method, interval) {
|
9
|
+
if (!interval) {
|
10
|
+
method();
|
11
|
+
return null;
|
12
|
+
}
|
13
|
+
return setTimeout(() => method(), interval);
|
14
|
+
}
|
package/Directions.web.js
DELETED
package/Swipeable.web.js
DELETED