@zeey4d/react-native-gesture-engine 1.0.0
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/README.md +246 -0
- package/dist/index.d.mts +1438 -0
- package/dist/index.d.ts +1438 -0
- package/dist/index.js +2448 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2401 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# @zeey4d/react-native-gesture-engine
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@zeey4d/react-native-gesture-engine)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
6
|
+
|
|
7
|
+
A **modular, production-ready 6-layer event-driven gesture engine** for React Native. Supports touch, sensor, hardware, and sequence-based gestures with conflict resolution, action dispatch, and multi-modal feedback.
|
|
8
|
+
|
|
9
|
+
## โจ Features
|
|
10
|
+
|
|
11
|
+
- ๐๏ธ **6-Layer Architecture** โ Input โ Processing โ Recognition โ Conflict Resolution โ Action โ Feedback
|
|
12
|
+
- ๐ฏ **12 Built-in Recognizers** โ Tap, DoubleTap, Pan, Pinch, Rotation, EdgeSwipe, Corner, Shake, Tilt, WristFlick, Sequence, Symbol ($1 Unistroke)
|
|
13
|
+
- โก **Event-Driven** โ Typed pub/sub EventBus with compile-time channelโpayload safety
|
|
14
|
+
- ๐ **Conflict Resolution** โ Priority queue + exclusive lock manager for gesture arbitration
|
|
15
|
+
- ๐ฑ **Sensor Support** โ Accelerometer & Gyroscope via expo-sensors with configurable intervals
|
|
16
|
+
- ๐ฎ **Haptic Feedback** โ expo-haptics with Vibration fallback
|
|
17
|
+
- โฟ **Accessibility** โ Automatic screen reader announcements
|
|
18
|
+
- ๐ช **React Hooks** โ `useGestureEngine`, `useShakeGesture`, `useEdgeSwipe`, `useGestureSequence`
|
|
19
|
+
- ๐งฉ **Extensible** โ Create custom recognizers by extending `BaseRecognizer`
|
|
20
|
+
- ๐ **Battery-Conscious** โ Throttled sensors, lazy initialization, ring buffer eviction
|
|
21
|
+
|
|
22
|
+
## ๐ฆ Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install @zeey4d/react-native-gesture-engine
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Peer Dependencies
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install react-native-gesture-handler react-native-reanimated expo-sensors
|
|
32
|
+
# Optional:
|
|
33
|
+
npm install expo-haptics
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
| Package | Version |
|
|
37
|
+
|---------|---------|
|
|
38
|
+
| `react` | >=18.0.0 |
|
|
39
|
+
| `react-native` | >=0.70.0 |
|
|
40
|
+
| `react-native-gesture-handler` | >=2.20.0 |
|
|
41
|
+
| `react-native-reanimated` | >=3.0.0 |
|
|
42
|
+
| `expo-sensors` | >=13.0.0 |
|
|
43
|
+
| `expo-haptics` | >=13.0.0 *(optional)* |
|
|
44
|
+
|
|
45
|
+
## ๐ Quick Start
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import {
|
|
49
|
+
GestureEngine,
|
|
50
|
+
ShakeRecognizer,
|
|
51
|
+
EdgeSwipeRecognizer,
|
|
52
|
+
CustomAction,
|
|
53
|
+
HapticFeedback,
|
|
54
|
+
} from '@zeey4d/react-native-gesture-engine';
|
|
55
|
+
|
|
56
|
+
// Create engine
|
|
57
|
+
const engine = new GestureEngine({
|
|
58
|
+
sensorInterval: 100,
|
|
59
|
+
hapticEnabled: true,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Register recognizers
|
|
63
|
+
const shake = new ShakeRecognizer(engine.eventBus, { threshold: 1.5 });
|
|
64
|
+
engine.registerRecognizer(shake);
|
|
65
|
+
|
|
66
|
+
const edgeSwipe = new EdgeSwipeRecognizer(engine.eventBus, {
|
|
67
|
+
edge: 'left',
|
|
68
|
+
minDistance: 50,
|
|
69
|
+
screenWidth: 400,
|
|
70
|
+
});
|
|
71
|
+
engine.registerRecognizer(edgeSwipe);
|
|
72
|
+
|
|
73
|
+
// Register actions
|
|
74
|
+
engine.registerAction('shake', new CustomAction('log', () => {
|
|
75
|
+
console.log('Device shaken!');
|
|
76
|
+
}));
|
|
77
|
+
|
|
78
|
+
// Add feedback
|
|
79
|
+
engine.registerFeedback(new HapticFeedback());
|
|
80
|
+
|
|
81
|
+
// Start
|
|
82
|
+
engine.start();
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## ๐ช React Hooks
|
|
86
|
+
|
|
87
|
+
### useGestureEngine
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
import { useGestureEngine, ShakeRecognizer } from '@zeey4d/react-native-gesture-engine';
|
|
91
|
+
|
|
92
|
+
function App() {
|
|
93
|
+
const { engine, isReady } = useGestureEngine({
|
|
94
|
+
sensorInterval: 100,
|
|
95
|
+
hapticEnabled: true,
|
|
96
|
+
recognizers: [new ShakeRecognizer(eventBus, { threshold: 1.5 })],
|
|
97
|
+
actions: { 'shake': [new CustomAction('alert', () => alert('Shaken!'))] },
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return <View>{isReady && <Text>Engine Ready</Text>}</View>;
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### useShakeGesture
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
useShakeGesture({
|
|
108
|
+
threshold: 1.5,
|
|
109
|
+
cooldownMs: 1000,
|
|
110
|
+
onShake: () => console.log('Device shaken!'),
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### useEdgeSwipe
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
useEdgeSwipe({
|
|
118
|
+
edge: 'left',
|
|
119
|
+
minDistance: 50,
|
|
120
|
+
onSwipe: (event) => navigation.goBack(),
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### useGestureSequence
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
useGestureSequence({
|
|
128
|
+
sequence: ['tap', 'tap', 'edge-swipe-right'],
|
|
129
|
+
timeoutMs: 800,
|
|
130
|
+
onComplete: () => console.log('Secret gesture unlocked!'),
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## ๐๏ธ Architecture
|
|
135
|
+
|
|
136
|
+
```mermaid
|
|
137
|
+
graph TD
|
|
138
|
+
A["Layer 1: Input"] -->|InputEvent| B["Layer 2: Processing"]
|
|
139
|
+
B -->|ProcessedSample| C["Layer 3: Recognition"]
|
|
140
|
+
C -->|GestureEvent| D["Layer 4: Conflict Resolution"]
|
|
141
|
+
D -->|Resolved GestureEvent| E["Layer 5: Actions"]
|
|
142
|
+
E -->|Dispatched GestureEvent| F["Layer 6: Feedback"]
|
|
143
|
+
|
|
144
|
+
A1["TouchInputProvider"] --> A
|
|
145
|
+
A2["SensorInputProvider"] --> A
|
|
146
|
+
A3["HardwareInputProvider"] --> A
|
|
147
|
+
|
|
148
|
+
B1["NoiseFilter"] --> B
|
|
149
|
+
B2["VelocityCalculator"] --> B
|
|
150
|
+
B3["AngleDetector"] --> B
|
|
151
|
+
B4["StreamBuffer"] --> B
|
|
152
|
+
|
|
153
|
+
C1["TapRecognizer"] --> C
|
|
154
|
+
C2["ShakeRecognizer"] --> C
|
|
155
|
+
C3["EdgeSwipeRecognizer"] --> C
|
|
156
|
+
C4["SymbolRecognizer"] --> C
|
|
157
|
+
|
|
158
|
+
D1["PriorityQueue"] --> D
|
|
159
|
+
D2["LockManager"] --> D
|
|
160
|
+
|
|
161
|
+
E1["NavigationAction"] --> E
|
|
162
|
+
E2["CustomAction"] --> E
|
|
163
|
+
|
|
164
|
+
F1["HapticFeedback"] --> F
|
|
165
|
+
F2["AccessibilityFeedback"] --> F
|
|
166
|
+
|
|
167
|
+
style A fill:#4CAF50,color:#fff
|
|
168
|
+
style B fill:#2196F3,color:#fff
|
|
169
|
+
style C fill:#FF9800,color:#fff
|
|
170
|
+
style D fill:#F44336,color:#fff
|
|
171
|
+
style E fill:#9C27B0,color:#fff
|
|
172
|
+
style F fill:#00BCD4,color:#fff
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## ๐ Recognizer Reference
|
|
176
|
+
|
|
177
|
+
| Recognizer | Type | Name | Key Config |
|
|
178
|
+
|------------|------|------|------------|
|
|
179
|
+
| `TapRecognizer` | Discrete | `tap` | `maxDuration`, `maxDistance` |
|
|
180
|
+
| `DoubleTapRecognizer` | Discrete | `double-tap` | `maxInterval`, `maxDistance` |
|
|
181
|
+
| `PanRecognizer` | Continuous | `pan` | `minDistance` |
|
|
182
|
+
| `PinchRecognizer` | Continuous | `pinch` | `minScale` |
|
|
183
|
+
| `RotationRecognizer` | Continuous | `rotation` | `minRotation` |
|
|
184
|
+
| `EdgeSwipeRecognizer` | Spatial | `edge-swipe-{edge}` | `edge`, `edgeZoneWidth`, `minDistance`, `minVelocity` |
|
|
185
|
+
| `CornerRecognizer` | Spatial | `corner-{corner}` | `corner`, `cornerZoneSize` |
|
|
186
|
+
| `ShakeRecognizer` | Sensor | `shake` | `threshold`, `consecutiveSamples`, `cooldownMs` |
|
|
187
|
+
| `TiltRecognizer` | Sensor | `tilt` | `tiltThreshold`, `cooldownMs` |
|
|
188
|
+
| `WristFlickRecognizer` | Sensor | `wrist-flick` | `angularVelocityThreshold`, `cooldownMs` |
|
|
189
|
+
| `SequenceRecognizer` | Sequence | `sequence:{names}` | `sequence[]`, `timeoutMs` |
|
|
190
|
+
| `SymbolRecognizer` | Symbolic | `symbol` | `templates`, `minConfidence` |
|
|
191
|
+
|
|
192
|
+
## ๐งฉ Custom Recognizer Guide
|
|
193
|
+
|
|
194
|
+
Extend `BaseRecognizer` to create your own:
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
import { BaseRecognizer, ProcessedSample, IEventBus } from '@zeey4d/react-native-gesture-engine';
|
|
198
|
+
|
|
199
|
+
class LongPressRecognizer extends BaseRecognizer {
|
|
200
|
+
private timer: ReturnType<typeof setTimeout> | null = null;
|
|
201
|
+
private durationMs: number;
|
|
202
|
+
|
|
203
|
+
constructor(eventBus: IEventBus, durationMs = 500) {
|
|
204
|
+
super('long-press', eventBus, { priority: 15, isExclusive: true });
|
|
205
|
+
this.durationMs = durationMs;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
onProcessedSample(sample: ProcessedSample): void {
|
|
209
|
+
if (!this.enabled) return;
|
|
210
|
+
// Your recognition logic here...
|
|
211
|
+
// Use: this.transitionToPossible(), this.transitionToBegan({ ... }), etc.
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
override reset(): void {
|
|
215
|
+
super.reset();
|
|
216
|
+
if (this.timer) clearTimeout(this.timer);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Available state transitions:
|
|
222
|
+
- `transitionToPossible()` โ Gesture might be starting
|
|
223
|
+
- `transitionToBegan(metadata)` โ Gesture recognized, emit event
|
|
224
|
+
- `transitionToChanged(metadata)` โ Continuous gesture update
|
|
225
|
+
- `transitionToEnded(metadata)` โ Gesture completed
|
|
226
|
+
- `transitionToFailed()` โ Gesture didn't match
|
|
227
|
+
- `transitionToCancelled()` โ Gesture interrupted
|
|
228
|
+
|
|
229
|
+
## โก Performance Tips
|
|
230
|
+
|
|
231
|
+
1. **Sensor interval**: Use 100ms (10Hz) for battery efficiency. Only go to 16ms (60Hz) for real-time tracking.
|
|
232
|
+
2. **Lazy providers**: Sensor providers only subscribe when `start()` is called.
|
|
233
|
+
3. **Ring buffer**: `StreamBuffer` auto-evicts samples older than 400ms โ O(1) per operation.
|
|
234
|
+
4. **Exclusive locks**: Conflict resolver stops redundant parallel processing.
|
|
235
|
+
5. **Ref-based hooks**: Engine lives outside React tree via `useRef` โ no unnecessary re-renders.
|
|
236
|
+
|
|
237
|
+
## ๐งช Testing
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
npm test # Run tests
|
|
241
|
+
npm run test:coverage # Run with coverage report
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## ๐ License
|
|
245
|
+
|
|
246
|
+
MIT ยฉ [zeey4d](https://github.com/zeey4d)
|