react-native-gesture-handler 1.2.1 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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