@renegades/react-native-tickle 0.1.0 → 0.1.2
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 +51 -64
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
<img width="1536" height="1024" alt="banner" src="https://github.com/user-attachments/assets/61f1f3bf-24dc-4e0d-ba6a-4dce3a548c96" />
|
|
2
|
+
|
|
3
|
+
# @renegades/react-native-tickle
|
|
2
4
|
|
|
3
5
|
AHAP-style haptics (transient + continuous) on top of [Nitro Modules](https://nitro.margelo.com/) — the core functions are **UI-thread friendly** (`'worklet'`).
|
|
4
6
|
|
|
@@ -6,17 +8,17 @@ AHAP-style haptics (transient + continuous) on top of [Nitro Modules](https://ni
|
|
|
6
8
|
|
|
7
9
|
Complete flexibility over iOS haptics with synchronous UI-thread execution.
|
|
8
10
|
|
|
9
|
-
> iOS only (Core Haptics). Android support could be added in the future, but it
|
|
11
|
+
> iOS only (Core Haptics). Android support could be added in the future, but it's currently unplanned.
|
|
10
12
|
|
|
11
13
|
## Installation
|
|
12
14
|
|
|
13
15
|
```sh
|
|
14
|
-
npm i react-native-tickle react-native-nitro-modules
|
|
16
|
+
npm i @renegades/react-native-tickle react-native-nitro-modules
|
|
15
17
|
```
|
|
16
18
|
|
|
17
19
|
## Concepts (what to use when)
|
|
18
20
|
|
|
19
|
-
- **Transient**: Instant
|
|
21
|
+
- **Transient**: Instant "click/tap" events. No duration — you trigger them at a point in time.
|
|
20
22
|
- **Continuous (pattern)**: Time-based patterns you _can_ define ahead of time. You provide **events** (with `duration`) and optionally **curves** (automation over time).
|
|
21
23
|
- **Continuous player (real-time)**: For _unpredictable_ input (gesture position, scroll velocity, real-time data). You create a player once, then **start → update (many times) → stop**.
|
|
22
24
|
|
|
@@ -24,10 +26,29 @@ npm i react-native-tickle react-native-nitro-modules
|
|
|
24
26
|
|
|
25
27
|
On iOS Core Haptics, a pattern is made of two different building blocks:
|
|
26
28
|
|
|
27
|
-
- **Events**: things that happen (transient
|
|
28
|
-
- **Curves**: how parameters (intensity/sharpness) evolve over time via control points, independent of
|
|
29
|
+
- **Events**: things that happen (transient "ticks" or continuous segments) at a `relativeTime`, with base `intensity`/`sharpness`.
|
|
30
|
+
- **Curves**: how parameters (intensity/sharpness) evolve over time via control points, independent of "what event" is currently playing.
|
|
31
|
+
|
|
32
|
+
They're separate because they're different object types in Core Haptics (events vs parameter curves) and they serve different jobs: **events define the structure**, **curves define the modulation**. You often combine both in one pattern.
|
|
33
|
+
|
|
34
|
+
## Haptix Studio
|
|
35
|
+
|
|
36
|
+
<table>
|
|
37
|
+
<tr>
|
|
38
|
+
<td>
|
|
39
|
+
<img src="https://github.com/user-attachments/assets/9a0edcfa-37ee-440d-9ae3-48fdcce59780" alt="Haptix Studio" width="280" />
|
|
40
|
+
</td>
|
|
41
|
+
<td>
|
|
29
42
|
|
|
30
|
-
|
|
43
|
+
Try out **Haptix Studio** to see what's possible with haptics.
|
|
44
|
+
|
|
45
|
+
Play with continuous patterns and real-time updates, then export your creations directly into code.
|
|
46
|
+
|
|
47
|
+
[Download on Test Flight](https://testflight.apple.com/join/HJn3mbb3)
|
|
48
|
+
|
|
49
|
+
</td>
|
|
50
|
+
</tr>
|
|
51
|
+
</table>
|
|
31
52
|
|
|
32
53
|
## Usage
|
|
33
54
|
|
|
@@ -36,7 +57,7 @@ They’re separate because they’re different object types in Core Haptics (eve
|
|
|
36
57
|
Wrap your app inside `HapticProvider`. This initializes the engine and automatically destroys it when the app goes to background.
|
|
37
58
|
|
|
38
59
|
```tsx
|
|
39
|
-
import { HapticProvider } from 'react-native-tickle';
|
|
60
|
+
import { HapticProvider } from '@renegades/react-native-tickle';
|
|
40
61
|
|
|
41
62
|
export function App() {
|
|
42
63
|
return <HapticProvider>{/* {Rest of your app} */}</HapticProvider>;
|
|
@@ -48,7 +69,7 @@ export function App() {
|
|
|
48
69
|
Play a transient:
|
|
49
70
|
|
|
50
71
|
```ts
|
|
51
|
-
import { startHaptic } from 'react-native-tickle';
|
|
72
|
+
import { startHaptic } from '@renegades/react-native-tickle';
|
|
52
73
|
|
|
53
74
|
startHaptic(
|
|
54
75
|
[
|
|
@@ -68,7 +89,7 @@ startHaptic(
|
|
|
68
89
|
Play a continuous pattern (events + curves together):
|
|
69
90
|
|
|
70
91
|
```ts
|
|
71
|
-
import { startHaptic } from 'react-native-tickle';
|
|
92
|
+
import { startHaptic } from '@renegades/react-native-tickle';
|
|
72
93
|
|
|
73
94
|
startHaptic(
|
|
74
95
|
[
|
|
@@ -99,7 +120,7 @@ startHaptic(
|
|
|
99
120
|
Combine transient + continuous in one pattern:
|
|
100
121
|
|
|
101
122
|
```ts
|
|
102
|
-
import { startHaptic } from 'react-native-tickle';
|
|
123
|
+
import { startHaptic } from '@renegades/react-native-tickle';
|
|
103
124
|
|
|
104
125
|
startHaptic(
|
|
105
126
|
[
|
|
@@ -129,10 +150,8 @@ startHaptic(
|
|
|
129
150
|
|
|
130
151
|
Use this when you _can't_ predefine a pattern. You start the player, update it in real time, then stop it.
|
|
131
152
|
|
|
132
|
-
**Using the hook (recommended):**
|
|
133
|
-
|
|
134
153
|
```tsx
|
|
135
|
-
import { useContinuousPlayer } from 'react-native-tickle';
|
|
154
|
+
import { useContinuousPlayer } from '@renegades/react-native-tickle';
|
|
136
155
|
|
|
137
156
|
function MyComponent() {
|
|
138
157
|
const { start, stop, update } = useContinuousPlayer('my-player', 1.0, 0.5);
|
|
@@ -150,44 +169,12 @@ function MyComponent() {
|
|
|
150
169
|
}
|
|
151
170
|
```
|
|
152
171
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
```ts
|
|
156
|
-
import {
|
|
157
|
-
createContinuousPlayer,
|
|
158
|
-
startContinuousPlayer,
|
|
159
|
-
updateContinuousPlayer,
|
|
160
|
-
stopContinuousPlayer,
|
|
161
|
-
destroyContinuousPlayer,
|
|
162
|
-
} from 'react-native-tickle';
|
|
163
|
-
|
|
164
|
-
const PLAYER_ID = 'my-player';
|
|
165
|
-
|
|
166
|
-
createContinuousPlayer(PLAYER_ID, 1.0, 0.5);
|
|
167
|
-
startContinuousPlayer(PLAYER_ID);
|
|
168
|
-
updateContinuousPlayer(PLAYER_ID, 0.8, 0.1);
|
|
169
|
-
stopContinuousPlayer(PLAYER_ID);
|
|
170
|
-
destroyContinuousPlayer(PLAYER_ID);
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
### Opt out of the provider (manual engine control)
|
|
174
|
-
|
|
175
|
-
If you don’t want the provider behavior:
|
|
176
|
-
|
|
177
|
-
```ts
|
|
178
|
-
import { initializeEngine, destroyEngine } from 'react-native-tickle';
|
|
179
|
-
|
|
180
|
-
initializeEngine();
|
|
181
|
-
// ...
|
|
182
|
-
destroyEngine();
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
### Stop everything (recommended in screen cleanups)
|
|
172
|
+
### Stop everything
|
|
186
173
|
|
|
187
|
-
|
|
174
|
+
If users can navigate away from a screen while haptics are still playing, call `stopAllHaptics()` in your cleanup to stop them.
|
|
188
175
|
|
|
189
176
|
```ts
|
|
190
|
-
import { stopAllHaptics } from 'react-native-tickle';
|
|
177
|
+
import { stopAllHaptics } from '@renegades/react-native-tickle';
|
|
191
178
|
import { useEffect } from 'react';
|
|
192
179
|
|
|
193
180
|
export function SomeScreen() {
|
|
@@ -200,10 +187,8 @@ export function SomeScreen() {
|
|
|
200
187
|
|
|
201
188
|
Disable haptics globally for users who prefer no haptic feedback. The setting is **persisted** across app restarts. When disabled, all haptic calls become no-ops.
|
|
202
189
|
|
|
203
|
-
**Using the hook (recommended):**
|
|
204
|
-
|
|
205
190
|
```tsx
|
|
206
|
-
import { useHapticsEnabled } from 'react-native-tickle';
|
|
191
|
+
import { useHapticsEnabled } from '@renegades/react-native-tickle';
|
|
207
192
|
|
|
208
193
|
function SettingsScreen() {
|
|
209
194
|
const [hapticsEnabled, setHapticsEnabled] = useHapticsEnabled();
|
|
@@ -212,26 +197,16 @@ function SettingsScreen() {
|
|
|
212
197
|
}
|
|
213
198
|
```
|
|
214
199
|
|
|
215
|
-
**Manual control:**
|
|
216
|
-
|
|
217
|
-
```ts
|
|
218
|
-
import { setHapticsEnabled, getHapticsEnabled } from 'react-native-tickle';
|
|
219
|
-
|
|
220
|
-
const isEnabled = getHapticsEnabled(); // true by default
|
|
221
|
-
setHapticsEnabled(false); // Disable all haptics
|
|
222
|
-
setHapticsEnabled(true); // Re-enable haptics
|
|
223
|
-
```
|
|
224
|
-
|
|
225
200
|
### System haptics (predefined OS-level feedback)
|
|
226
201
|
|
|
227
|
-
While the main purpose of this package is to support AHAP-style patterns (transient + continuous haptics with curves), system haptics are also available for completeness. These are simple, predefined OS-level feedback types that don't require pattern definitions.
|
|
202
|
+
While the main purpose of this package is to support AHAP-style patterns (transient + continuous haptics with curves), system haptics are also available for completeness. These are simple, predefined OS-level feedback types that don't require pattern definitions. These methods are also UI-thread friendly.
|
|
228
203
|
|
|
229
204
|
```ts
|
|
230
205
|
import {
|
|
231
206
|
triggerImpact,
|
|
232
207
|
triggerNotification,
|
|
233
208
|
triggerSelection,
|
|
234
|
-
} from 'react-native-tickle';
|
|
209
|
+
} from '@renegades/react-native-tickle';
|
|
235
210
|
|
|
236
211
|
// Impact feedback - simulates a physical collision
|
|
237
212
|
triggerImpact('light'); // 'light' | 'medium' | 'heavy' | 'soft' | 'rigid'
|
|
@@ -243,6 +218,14 @@ triggerNotification('success'); // 'success' | 'warning' | 'error'
|
|
|
243
218
|
triggerSelection();
|
|
244
219
|
```
|
|
245
220
|
|
|
221
|
+
### Advanced control
|
|
222
|
+
|
|
223
|
+
For fine-grained control over the haptic API, you can opt out of the managed hooks and call the underlying functions directly:
|
|
224
|
+
|
|
225
|
+
- **Engine lifecycle**: `initializeEngine()` / `destroyEngine()` instead of `HapticProvider`
|
|
226
|
+
- **Enable/disable toggle**: `setHapticsEnabled()` / `getHapticsEnabled()` instead of `useHapticsEnabled()`
|
|
227
|
+
- **Continuous player**: `createContinuousPlayer()` / `startContinuousPlayer()` / `updateContinuousPlayer()` / `stopContinuousPlayer()` / `destroyContinuousPlayer()` instead of `useContinuousPlayer()`
|
|
228
|
+
|
|
246
229
|
## API
|
|
247
230
|
|
|
248
231
|
| Function | Purpose |
|
|
@@ -341,6 +324,10 @@ Each call creates an isolated pattern/player — curves from one won't affect ev
|
|
|
341
324
|
|
|
342
325
|
> **Note:** The library automatically resets control values to `1.0` at the end of each continuous event, so transients **after** a continuous event finishes are not affected. This limitation only applies to transients **during** a continuous event with active curves.
|
|
343
326
|
|
|
327
|
+
## Acknowledgment
|
|
328
|
+
|
|
329
|
+
Shoutout to [Jai](https://github.com/jaic231) for kicking off the Swift implementation 🙌
|
|
330
|
+
|
|
344
331
|
## Contributing
|
|
345
332
|
|
|
346
333
|
- [Development workflow](CONTRIBUTING.md#development-workflow)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@renegades/react-native-tickle",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Build and fine-tune haptic patterns with real-time previews",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -34,9 +34,8 @@
|
|
|
34
34
|
"!**/.*"
|
|
35
35
|
],
|
|
36
36
|
"scripts": {
|
|
37
|
-
"postinstall": "bash scripts/postinstall.sh",
|
|
38
37
|
"clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
|
|
39
|
-
"prepare": "bob build",
|
|
38
|
+
"prepare": "bun --yes nitrogen && bob build",
|
|
40
39
|
"nitrogen": "nitrogen",
|
|
41
40
|
"typecheck": "tsc",
|
|
42
41
|
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
@@ -99,7 +98,7 @@
|
|
|
99
98
|
"peerDependencies": {
|
|
100
99
|
"react": "*",
|
|
101
100
|
"react-native": "*",
|
|
102
|
-
"react-native-nitro-modules": "
|
|
101
|
+
"react-native-nitro-modules": "*"
|
|
103
102
|
},
|
|
104
103
|
"packageManager": "bun@1.1.38",
|
|
105
104
|
"react-native-builder-bob": {
|