react-native-haptic-feedback
Version:
Basic haptic feedback for iOS and android
130 lines (114 loc) • 4.14 kB
text/typescript
/**
* Web platform implementation of react-native-haptic-feedback.
* Uses the Web Vibration API (navigator.vibrate) where supported.
* Silently no-ops on browsers that don't support vibration.
*/
import { HapticFeedbackTypes } from "./types";
import type { HapticOptions, HapticEvent, SystemHapticStatus } from "./types";
// Duration map for single-pulse haptics (milliseconds)
const WEB_DURATION: Partial<Record<string, number | number[]>> = {
[]: 20,
[]: 40,
[]: 60,
[]: 50,
[]: 15,
[]: 15,
[]: [30, 40, 20],
[]: [20, 40, 30],
[]: [20, 30, 30, 30, 40],
[]: 20,
[]: [20, 40, 20],
[]: 50,
[]: 15,
[]: 15,
[]: 20,
[]: 20,
[]: 10,
[]: 20,
[]: 40,
[]: 10,
[]: 20,
[]: 10,
[]: [30, 40, 20],
[]: [50, 40, 30],
[]: 15,
[]: 20,
[]: 10,
[]: 8,
[]: [15, 30, 25],
[]: [25, 30, 15],
[]: 15,
[]: 20,
[]: 10,
// noHaptics: intentionally absent — no vibration
};
function vibrate(pattern: number | number[]): void {
if (typeof navigator !== "undefined" && "vibrate" in navigator) {
navigator.vibrate(pattern);
}
}
let _enabled = true;
const RNHapticFeedback = {
setEnabled(value: boolean): void {
_enabled = value;
},
isEnabled(): boolean {
return _enabled;
},
trigger(
type:
| keyof typeof HapticFeedbackTypes
| HapticFeedbackTypes = HapticFeedbackTypes.selection,
_options: HapticOptions = {},
): void {
if (!_enabled) return;
const pattern = WEB_DURATION[type as string] ?? 40;
vibrate(pattern);
},
stop(): void {
if (typeof navigator !== "undefined" && "vibrate" in navigator) {
navigator.vibrate(0);
}
},
isSupported(): boolean {
return typeof navigator !== "undefined" && "vibrate" in navigator;
},
triggerPattern(events: HapticEvent[], _options: HapticOptions = {}): void {
if (!_enabled) return;
if (!events.length) return;
const sorted = [...events].sort((a, b) => a.time - b.time);
const pattern: number[] = [];
let prevEnd = 0;
for (const evt of sorted) {
const gap = Math.max(0, evt.time - prevEnd);
const duration = evt.duration ?? 50;
pattern.push(gap, duration);
prevEnd = evt.time + duration;
}
vibrate(pattern);
},
impact(
type:
| keyof typeof HapticFeedbackTypes
| HapticFeedbackTypes = HapticFeedbackTypes.impactMedium,
intensity = 0.7,
_options: HapticOptions = {},
): void {
if (!_enabled) return;
const base = WEB_DURATION[type as string] ?? 40;
const clamped = Math.max(0, Math.min(1, intensity));
const pattern = Array.isArray(base)
? base.map((d) => Math.round(d * clamped))
: Math.round(base * clamped);
vibrate(pattern);
},
playAHAP(_fileName: string): Promise<void> {
return Promise.resolve();
},
async getSystemHapticStatus(): Promise<SystemHapticStatus> {
const supported =
typeof navigator !== "undefined" && "vibrate" in navigator;
return { vibrationEnabled: supported, ringerMode: null };
},
};
export default RNHapticFeedback;