web-haptic-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/LICENSE +9 -0
- package/README.md +328 -0
- package/dist/index.d.mts +151 -0
- package/dist/index.mjs +1047 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright © 2026 Sumit Sahoo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<h1>📳 Web Haptic Engine</h1>
|
|
4
|
+
|
|
5
|
+
<p>A cross-platform haptic feedback engine for the web.<br>
|
|
6
|
+
Supports Android vibration, iOS Taptic feedback, audio impulse synthesis, drag haptics, and 23 built-in presets.</p>
|
|
7
|
+
|
|
8
|
+
<p>
|
|
9
|
+
<a href="https://www.npmjs.com/package/web-haptic-engine"><img src="https://img.shields.io/npm/v/web-haptic-engine" alt="npm version" /></a>
|
|
10
|
+
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-yellow.svg" alt="MIT License" /></a>
|
|
11
|
+
<img src="https://img.shields.io/badge/platform-web-blue" alt="Platform" />
|
|
12
|
+
<img src="https://img.shields.io/badge/typescript-strict-blue" alt="TypeScript" />
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## ✨ Features
|
|
20
|
+
|
|
21
|
+
| Category | Details |
|
|
22
|
+
| -------------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
23
|
+
| 📱 **Android Vibration** | Full `navigator.vibrate()` pattern support with intensity-scaled durations |
|
|
24
|
+
| 🍎 **iOS Taptic** | Exploits the `<input type="checkbox" switch>` toggle to trigger native Taptic Engine feedback |
|
|
25
|
+
| 🔊 **Audio Impulse Layer** | 8 synthesized AudioBuffer impulses (`tick`, `tap`, `thud`, `click`, `snap`, `buzz`, `confirm`, `harsh`) |
|
|
26
|
+
| 👆 **Drag Haptics** | Touchmove-driven haptic feedback with distance threshold — works reliably on iOS |
|
|
27
|
+
| 🎛️ **23 Presets** | Ready-to-use patterns: `success`, `warning`, `error`, `heartbeat`, `spring`, `buzz`, and more |
|
|
28
|
+
| 🔗 **Sequences** | Chain presets with delays, repeats, and custom gaps |
|
|
29
|
+
| 〰️ **Easing Functions** | `linear`, `easeIn`, `easeOut`, `easeInOut`, `bounce`, `spring` |
|
|
30
|
+
| 📦 **Zero Dependencies** | No external runtime dependencies — pure TypeScript |
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 📥 Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install web-haptic-engine
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pnpm add web-haptic-engine
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
yarn add web-haptic-engine
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 🚀 Quick Start
|
|
51
|
+
|
|
52
|
+
### Basic Usage
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { haptic } from "web-haptic-engine";
|
|
56
|
+
|
|
57
|
+
// Fire a preset
|
|
58
|
+
await haptic("success");
|
|
59
|
+
await haptic("heartbeat");
|
|
60
|
+
await haptic("click");
|
|
61
|
+
|
|
62
|
+
// Fire with custom intensity
|
|
63
|
+
await haptic("heavy", { intensity: 0.8 });
|
|
64
|
+
|
|
65
|
+
// Fire a raw duration (ms)
|
|
66
|
+
await haptic(50);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Using the Engine
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { HapticEngine } from "web-haptic-engine";
|
|
73
|
+
|
|
74
|
+
const engine = new HapticEngine({
|
|
75
|
+
throttleMs: 25,
|
|
76
|
+
audioLayer: true,
|
|
77
|
+
audioGain: 0.6,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Trigger presets
|
|
81
|
+
await engine.trigger("confirm");
|
|
82
|
+
await engine.trigger("buzz", { intensity: 1.0 });
|
|
83
|
+
|
|
84
|
+
// Sequences
|
|
85
|
+
await engine.sequence([{ preset: "rampUp" }, { preset: "confirm", delay: 200 }], {
|
|
86
|
+
repeat: 2,
|
|
87
|
+
repeatGap: 300,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Custom presets
|
|
91
|
+
engine.registerPreset("myPattern", {
|
|
92
|
+
pattern: [
|
|
93
|
+
{ duration: 20, intensity: 0.6 },
|
|
94
|
+
{ delay: 50, duration: 40, intensity: 1.0 },
|
|
95
|
+
],
|
|
96
|
+
impulse: "tap",
|
|
97
|
+
iosTicks: 2,
|
|
98
|
+
iosTickGap: 60,
|
|
99
|
+
});
|
|
100
|
+
await engine.trigger("myPattern");
|
|
101
|
+
|
|
102
|
+
// Cleanup
|
|
103
|
+
engine.destroy();
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 👆 Drag Haptics
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import { HapticEngine } from "web-haptic-engine";
|
|
110
|
+
|
|
111
|
+
const engine = new HapticEngine();
|
|
112
|
+
|
|
113
|
+
const drag = engine.drag({
|
|
114
|
+
fireDist: 18, // px between haptic fires
|
|
115
|
+
impulse: "tick", // audio impulse type
|
|
116
|
+
intensity: 0.6,
|
|
117
|
+
onTick: (velocity, ticks) => {
|
|
118
|
+
console.log(`Tick #${ticks} at ${velocity}px/s`);
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Bind to a DOM element
|
|
123
|
+
const unbind = drag.bind(document.getElementById("drag-area")!);
|
|
124
|
+
|
|
125
|
+
// Later: cleanup
|
|
126
|
+
unbind();
|
|
127
|
+
drag.destroyAll();
|
|
128
|
+
engine.destroy();
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 🎛️ Presets
|
|
134
|
+
|
|
135
|
+
| Preset | Description | Impulse |
|
|
136
|
+
| -------------- | ------------------------- | --------- |
|
|
137
|
+
| ✅ `success` | Ascending double-tap | `confirm` |
|
|
138
|
+
| ⚠️ `warning` | Two hesitant taps | `harsh` |
|
|
139
|
+
| ❌ `error` | Three rapid harsh taps | `harsh` |
|
|
140
|
+
| 👍 `confirm` | Strong double-tap confirm | `confirm` |
|
|
141
|
+
| 👎 `reject` | Harsh staccato triple | `harsh` |
|
|
142
|
+
| 🪶 `light` | Single light tap | `tick` |
|
|
143
|
+
| ⚖️ `medium` | Moderate tap | `tap` |
|
|
144
|
+
| 🏋️ `heavy` | Strong tap | `thud` |
|
|
145
|
+
| 🧸 `soft` | Cushioned tap | `tap` |
|
|
146
|
+
| 🔩 `rigid` | Hard crisp snap | `snap` |
|
|
147
|
+
| 🔘 `selection` | Subtle tick | `tick` |
|
|
148
|
+
| ⏱️ `tick` | Crisp tick | `click` |
|
|
149
|
+
| 🖱️ `click` | Ultra-short click | `click` |
|
|
150
|
+
| 🫰 `snap` | Sharp snap | `snap` |
|
|
151
|
+
| 👉 `nudge` | Two quick taps | `tap` |
|
|
152
|
+
| 🐝 `buzz` | Sustained vibration | `buzz` |
|
|
153
|
+
| 💓 `heartbeat` | Heartbeat rhythm | `thud` |
|
|
154
|
+
| 🧲 `spring` | Bouncy pulses | `tap` |
|
|
155
|
+
| 📈 `rampUp` | Escalating | `tap` |
|
|
156
|
+
| 📉 `rampDown` | Decreasing | `tap` |
|
|
157
|
+
| 💥 `thud` | Heavy impact | `thud` |
|
|
158
|
+
| 🎵 `trill` | Rapid flutter | `click` |
|
|
159
|
+
| 💗 `pulse` | Rhythmic pulse | `tap` |
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 📖 API Reference
|
|
164
|
+
|
|
165
|
+
### `HapticEngine`
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
const engine = new HapticEngine(options?: HapticEngineOptions)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
| Option | Type | Default | Description |
|
|
172
|
+
| ------------ | --------- | ------- | --------------------------- |
|
|
173
|
+
| `throttleMs` | `number` | `25` | Minimum ms between triggers |
|
|
174
|
+
| `audioLayer` | `boolean` | `true` | Enable audio impulse layer |
|
|
175
|
+
| `audioGain` | `number` | `0.6` | Master audio gain (0–1) |
|
|
176
|
+
|
|
177
|
+
#### 🔧 Methods
|
|
178
|
+
|
|
179
|
+
| Method | Description |
|
|
180
|
+
| ------------------------------ | ------------------------------- |
|
|
181
|
+
| `trigger(input?, options?)` | Fire a haptic pattern |
|
|
182
|
+
| `sequence(steps, options?)` | Play a sequence of presets |
|
|
183
|
+
| `drag(options?)` | Create a `DragHaptics` instance |
|
|
184
|
+
| `cancel()` | Cancel active haptic playback |
|
|
185
|
+
| `setEnabled(enabled)` | Enable/disable the engine |
|
|
186
|
+
| `setAudioLayer(enabled)` | Toggle audio layer |
|
|
187
|
+
| `setAudioGain(gain)` | Set audio gain (0–1) |
|
|
188
|
+
| `setThrottle(ms)` | Set throttle interval |
|
|
189
|
+
| `registerPreset(name, preset)` | Register a custom preset |
|
|
190
|
+
| `fireHapticTick(intensity)` | Fire a single platform tick |
|
|
191
|
+
| `fireImpulse(type, intensity)` | Fire a single audio impulse |
|
|
192
|
+
| `destroy()` | Clean up all resources |
|
|
193
|
+
|
|
194
|
+
#### 📊 Static Properties
|
|
195
|
+
|
|
196
|
+
| Property | Description |
|
|
197
|
+
| --------------------------------- | ------------------------------------------ |
|
|
198
|
+
| `HapticEngine.supportsVibration` | `true` if `navigator.vibrate` is available |
|
|
199
|
+
| `HapticEngine.supportsIOSHaptics` | `true` if iOS Taptic switch is supported |
|
|
200
|
+
| `HapticEngine.isSupported` | `true` if any haptic method is available |
|
|
201
|
+
|
|
202
|
+
### `DragHaptics`
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
const drag = engine.drag(options?: DragHapticsOptions)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
| Option | Type | Default | Description |
|
|
209
|
+
| ----------- | ------------- | -------- | --------------------------------------- |
|
|
210
|
+
| `fireDist` | `number` | `18` | Minimum px moved to trigger next haptic |
|
|
211
|
+
| `impulse` | `ImpulseType` | `'tick'` | Audio impulse type |
|
|
212
|
+
| `intensity` | `number` | `0.6` | Haptic intensity |
|
|
213
|
+
| `onTick` | `function` | — | Callback `(velocity, ticks) => void` |
|
|
214
|
+
|
|
215
|
+
### `haptic()` (convenience)
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
import { haptic } from "web-haptic-engine";
|
|
219
|
+
|
|
220
|
+
await haptic("success"); // preset name
|
|
221
|
+
await haptic(50); // raw duration ms
|
|
222
|
+
await haptic([20, 10, 30]); // pattern array
|
|
223
|
+
await haptic("heavy", { intensity: 1 }); // with options
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## 🌍 Platform Support
|
|
229
|
+
|
|
230
|
+
| Platform | Haptic Method | Audio |
|
|
231
|
+
| ----------------- | ------------------------------ | -------------------------------- |
|
|
232
|
+
| 📱 **Android** | `navigator.vibrate()` | AudioContext impulses |
|
|
233
|
+
| 🍎 **iOS Safari** | `<input switch>` Taptic toggle | AudioContext impulses |
|
|
234
|
+
| 🖥️ **Desktop** | — | AudioContext impulses (fallback) |
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## 🛠️ Tech Stack
|
|
239
|
+
|
|
240
|
+
| Tool | Purpose |
|
|
241
|
+
| --------------------------------------------- | --------------------- |
|
|
242
|
+
| [TypeScript](https://www.typescriptlang.org/) | Type-safe source code |
|
|
243
|
+
| [tsdown](https://tsdown.dev/) | Library bundling |
|
|
244
|
+
| [Vitest](https://vitest.dev/) | Unit testing |
|
|
245
|
+
| [Vite+](https://vite.dev/) | Unified toolchain |
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## 🧑💻 Development
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
# Install dependencies
|
|
253
|
+
vp install
|
|
254
|
+
|
|
255
|
+
# Build the library
|
|
256
|
+
vp pack
|
|
257
|
+
|
|
258
|
+
# Run tests
|
|
259
|
+
vp test
|
|
260
|
+
|
|
261
|
+
# Watch mode (rebuild on changes)
|
|
262
|
+
vp pack --watch
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## 🎮 Demo
|
|
268
|
+
|
|
269
|
+
An interactive demo is included in the `demo/` directory. It showcases all 23 presets, drag haptics, impulse buffers, sequences, and real-time controls for intensity and audio gain.
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
# Install dependencies (if not already done)
|
|
273
|
+
vp install
|
|
274
|
+
|
|
275
|
+
# Start the demo dev server
|
|
276
|
+
vp run demo
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
This launches a Vite dev server. Open the URL shown in the terminal (typically `http://localhost:5173`) in your browser. For the full haptic experience, open it on a mobile device — Android for vibration, iOS Safari for Taptic feedback. On desktop, audio impulses still play as a fallback.
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## 📁 Project Structure
|
|
284
|
+
|
|
285
|
+
```
|
|
286
|
+
web-haptic-engine/
|
|
287
|
+
├── src/
|
|
288
|
+
│ ├── core/ # Types, constants, easings & presets
|
|
289
|
+
│ ├── audio/ # Web Audio impulse synthesis & playback
|
|
290
|
+
│ ├── platform/ # Platform detection & adapters (Android, iOS)
|
|
291
|
+
│ ├── interactions/ # User interaction patterns (drag haptics)
|
|
292
|
+
│ ├── haptic-engine.ts # Main HapticEngine class & convenience helpers
|
|
293
|
+
│ └── index.ts # Public API exports
|
|
294
|
+
├── demo/
|
|
295
|
+
│ ├── index.html # Demo page
|
|
296
|
+
│ ├── main.ts # Demo app (imports from library)
|
|
297
|
+
│ └── vite.config.ts # Vite config for demo dev server
|
|
298
|
+
├── tests/
|
|
299
|
+
│ └── index.test.ts # Unit tests
|
|
300
|
+
├── tsdown.config.ts # Library build config
|
|
301
|
+
├── vite.config.ts # Vite+ unified config
|
|
302
|
+
├── tsconfig.json # TypeScript config
|
|
303
|
+
└── package.json
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## 💡 Acknowledgements
|
|
309
|
+
|
|
310
|
+
This project was initially inspired by [web-haptics](https://github.com/lochie/web-haptics) by [@lochie](https://github.com/lochie).
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## 🤝 Contributing
|
|
315
|
+
|
|
316
|
+
Contributions are welcome! Please see the [CONTRIBUTING.md](CONTRIBUTING.md) guide for details.
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## 📄 License
|
|
321
|
+
|
|
322
|
+
This project is licensed under the **MIT License** — feel free to use it for both personal and commercial purposes. See the [LICENSE](LICENSE) file for details.
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
<p align="center">
|
|
327
|
+
Built with ❤️ by <a href="https://github.com/sumitsahoo">Sumit Sahoo</a>
|
|
328
|
+
</p>
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
//#region src/core/easings.d.ts
|
|
2
|
+
declare const easings: {
|
|
3
|
+
readonly linear: (t: number) => number;
|
|
4
|
+
readonly easeIn: (t: number) => number;
|
|
5
|
+
readonly easeOut: (t: number) => number;
|
|
6
|
+
readonly easeInOut: (t: number) => number;
|
|
7
|
+
readonly bounce: (t: number) => number;
|
|
8
|
+
readonly spring: (t: number) => number;
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/core/types.d.ts
|
|
12
|
+
interface Vibration {
|
|
13
|
+
duration: number;
|
|
14
|
+
intensity?: number;
|
|
15
|
+
delay?: number;
|
|
16
|
+
}
|
|
17
|
+
type HapticPattern = number[] | Vibration[];
|
|
18
|
+
type EasingFn = (t: number) => number;
|
|
19
|
+
type ImpulseType = "tick" | "tap" | "thud" | "click" | "snap" | "buzz" | "confirm" | "harsh";
|
|
20
|
+
interface HapticPreset {
|
|
21
|
+
pattern: Vibration[];
|
|
22
|
+
description?: string;
|
|
23
|
+
/** Number of iOS switch-checkbox toggles to fire for this preset. */
|
|
24
|
+
iosTicks?: number;
|
|
25
|
+
/** Milliseconds between iOS ticks. Defaults to IOS_GAP (55 ms). */
|
|
26
|
+
iosTickGap?: number;
|
|
27
|
+
/** Audio impulse type to play alongside the haptic. */
|
|
28
|
+
impulse?: ImpulseType;
|
|
29
|
+
/** Easing curve applied to multi-step patterns. */
|
|
30
|
+
easing?: keyof typeof easings;
|
|
31
|
+
}
|
|
32
|
+
type HapticInput = number | string | HapticPattern | HapticPreset;
|
|
33
|
+
interface TriggerOptions {
|
|
34
|
+
intensity?: number;
|
|
35
|
+
audio?: boolean;
|
|
36
|
+
}
|
|
37
|
+
interface HapticEngineOptions {
|
|
38
|
+
debug?: boolean;
|
|
39
|
+
/** Minimum ms between trigger() calls. Default 25. */
|
|
40
|
+
throttleMs?: number;
|
|
41
|
+
/** Enable audio impulse layer. Default true. */
|
|
42
|
+
audioLayer?: boolean;
|
|
43
|
+
/** Master gain for audio impulses (0-1). Default 0.6. */
|
|
44
|
+
audioGain?: number;
|
|
45
|
+
}
|
|
46
|
+
interface SequenceStep {
|
|
47
|
+
preset: string;
|
|
48
|
+
delay?: number;
|
|
49
|
+
intensity?: number;
|
|
50
|
+
}
|
|
51
|
+
interface SequenceOptions {
|
|
52
|
+
repeat?: number;
|
|
53
|
+
repeatGap?: number;
|
|
54
|
+
}
|
|
55
|
+
interface DragHapticsOptions {
|
|
56
|
+
/** Minimum px moved since last fire to trigger next haptic (default: 18). */
|
|
57
|
+
fireDist?: number;
|
|
58
|
+
/** Audio impulse type for drag ticks (default: 'tick'). */
|
|
59
|
+
impulse?: ImpulseType;
|
|
60
|
+
/** Haptic/audio intensity 0-1 (default: 0.6). */
|
|
61
|
+
intensity?: number;
|
|
62
|
+
/** Callback fired on each drag tick with current velocity and tick count. */
|
|
63
|
+
onTick?: (velocity: number, ticks: number) => void;
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region src/core/presets.d.ts
|
|
67
|
+
declare const presets: Record<string, HapticPreset>;
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region src/interactions/drag-haptics.d.ts
|
|
70
|
+
declare class DragHaptics {
|
|
71
|
+
private engine;
|
|
72
|
+
private opts;
|
|
73
|
+
curX: number;
|
|
74
|
+
curY: number;
|
|
75
|
+
private lastFireX;
|
|
76
|
+
private lastFireY;
|
|
77
|
+
private lastFireT;
|
|
78
|
+
private active;
|
|
79
|
+
private tickCount;
|
|
80
|
+
private cleanup;
|
|
81
|
+
/** Set during a touch sequence to prevent mouse handlers from double-firing. */
|
|
82
|
+
private touchActive;
|
|
83
|
+
constructor(engine: HapticEngine, options?: DragHapticsOptions);
|
|
84
|
+
/** Attach drag-haptic listeners to an element. Returns an unbind function. */
|
|
85
|
+
bind(element: HTMLElement): () => void;
|
|
86
|
+
private distFromLastFire;
|
|
87
|
+
/** Unbind all elements and stop tracking. */
|
|
88
|
+
destroyAll(): void;
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/haptic-engine.d.ts
|
|
92
|
+
declare class HapticEngine {
|
|
93
|
+
private platform;
|
|
94
|
+
private iosPool;
|
|
95
|
+
private audio;
|
|
96
|
+
private useAudio;
|
|
97
|
+
private throttleMs;
|
|
98
|
+
private enabled;
|
|
99
|
+
private lastTriggerTime;
|
|
100
|
+
private activeAbort;
|
|
101
|
+
/** True if the device supports Android-style navigator.vibrate(). */
|
|
102
|
+
static readonly supportsVibration: boolean;
|
|
103
|
+
/** True if the device supports iOS switch-checkbox haptics. */
|
|
104
|
+
static readonly supportsIOSHaptics: boolean;
|
|
105
|
+
/** True if any form of haptic feedback is available. */
|
|
106
|
+
static readonly isSupported: boolean;
|
|
107
|
+
constructor(options?: HapticEngineOptions);
|
|
108
|
+
/**
|
|
109
|
+
* Fire a haptic + audio pattern. Input can be a preset name, duration (ms),
|
|
110
|
+
* Vibration[], number[] (alternating on/off), or a HapticPreset object.
|
|
111
|
+
*/
|
|
112
|
+
trigger(input?: HapticInput, options?: TriggerOptions): Promise<void>;
|
|
113
|
+
/** Play an audio impulse. Set force=true to bypass the audio-enabled toggle. */
|
|
114
|
+
fireImpulse(type: ImpulseType, intensity: number, force?: boolean): void;
|
|
115
|
+
/** Fire a single platform haptic tick (short vibrate or single iOS switch toggle). */
|
|
116
|
+
fireHapticTick(intensity: number): void;
|
|
117
|
+
/**
|
|
118
|
+
* Fire a drag-optimized haptic tick. Skips cancel()/vibrate(0) overhead.
|
|
119
|
+
* Android: velocity scales pulse duration (12-25ms) and count (1-3 taps).
|
|
120
|
+
* iOS: single switch toggle (requires user activation to produce Taptic).
|
|
121
|
+
*/
|
|
122
|
+
fireDragTick(intensity: number, velocity: number): void;
|
|
123
|
+
/** Fire a drag tick only on Android (vibration platform). No-op on iOS. */
|
|
124
|
+
fireDragTickIfVibration(intensity: number, velocity: number): void;
|
|
125
|
+
/** Play a sequence of preset steps with optional delays and repeats. */
|
|
126
|
+
sequence(steps: SequenceStep[], options?: SequenceOptions): Promise<void>;
|
|
127
|
+
/** Create a DragHaptics instance bound to this engine. */
|
|
128
|
+
drag(options?: DragHapticsOptions): DragHaptics;
|
|
129
|
+
/** Cancel any in-progress pattern or vibration. */
|
|
130
|
+
cancel(): void;
|
|
131
|
+
setEnabled(e: boolean): void;
|
|
132
|
+
get isEnabled(): boolean;
|
|
133
|
+
setAudioLayer(e: boolean): void;
|
|
134
|
+
setAudioGain(g: number): void;
|
|
135
|
+
setThrottle(ms: number): void;
|
|
136
|
+
registerPreset(name: string, preset: HapticPreset): void;
|
|
137
|
+
/** Clean up all resources (audio context, iOS DOM elements). */
|
|
138
|
+
destroy(): void;
|
|
139
|
+
/** Resolve any HapticInput into a Vibration[] and optional preset metadata. */
|
|
140
|
+
private resolveInput;
|
|
141
|
+
private fireAndroid;
|
|
142
|
+
private fireIOS;
|
|
143
|
+
/** Cancellable setTimeout wrapped in a Promise. */
|
|
144
|
+
private sleep;
|
|
145
|
+
}
|
|
146
|
+
/** Get or create the shared default engine instance. */
|
|
147
|
+
declare function getDefaultEngine(opts?: HapticEngineOptions): HapticEngine;
|
|
148
|
+
/** Fire a one-shot haptic using the default engine. */
|
|
149
|
+
declare function haptic(input?: HapticInput, options?: TriggerOptions): Promise<void>;
|
|
150
|
+
//#endregion
|
|
151
|
+
export { DragHaptics, type DragHapticsOptions, type EasingFn, HapticEngine, type HapticEngineOptions, type HapticInput, type HapticPattern, type HapticPreset, type ImpulseType, type SequenceOptions, type SequenceStep, type TriggerOptions, type Vibration, easings, getDefaultEngine, haptic, presets };
|