Expo Haptics 触觉反馈模块实战指南:iOS 触觉引擎、Android 振动与 Web Vibration 的统一封装
2026/9/10 20:25:42 网站建设 项目流程

Expo Haptics 触觉反馈模块实战指南:iOS 触觉引擎、Android 振动与 Web Vibration 的统一封装

【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo

expo-haptics 是 Expo 官方提供的触觉反馈模块,用于在 React Native 应用中触发系统级物理反馈:iOS 使用系统触觉引擎(Haptics Engine),Android 使用振动效果,Web 端使用 Web Vibration API。本指南将围绕该模块的安装配置、完整 API 用法与各平台底层实现原理展开,帮助你在一行代码内为用户提供高质量的触感交互体验,并理解原生层(Swift / Kotlin)与 Web 层的具体工作方式。

一、模块概览:一套 API,三种平台实现

从 packages/expo-haptics/package.json 的定义可以看出,本模块的核心定位是:"Provides access to the system's haptics engine on iOS, vibration effects on Android, and Web Vibration API on web",即:

  • iOS:调用系统触觉引擎(UINotificationFeedbackGeneratorUIImpactFeedbackGeneratorUISelectionFeedbackGenerator),提供细腻、真实的触感;
  • Android:通过系统Vibrator产生振动波形,或直接调用View.performHapticFeedback触发系统预置触觉反馈;
  • Web:基于 Web Vibration API(navigator.vibrate),并在不支持的 iOS Safari 上采用隐藏 switch 元素触发原生触感的降级方案。

模块对外暴露的 TypeScript 入口为 packages/expo-haptics/src/Haptics.ts,其导出与类型定义(packages/expo-haptics/src/Haptics.types.ts)完全对齐,便于 DOM 组件与@expo/dom-webview场景下的统一使用(该对齐工作记录在模块 CHANGELOG.md 的 14.0.0 版本说明中)。

二、安装与平台配置

2.1 托管(Managed)Expo 项目

对于托管项目,直接使用 Expo CLI 的版本对齐命令安装即可,它会自动选择与当前 SDK 匹配的版本:

npx expo install expo-haptics

安装后无需任何手动配置,模块即可开箱即用。

2.2 纯 React Native(Bare)项目

纯 React Native 项目需要先确保已经安装并配置好expo包,然后按以下步骤操作:

第一步:添加 npm 依赖

npx expo install expo-haptics

第二步:配置 Android

该模块需要控制设备振动的权限,权限会在构建时自动添加。查看模块自带的 packages/expo-haptics/android/src/main/AndroidManifest.xml,可以看到它声明了:

<uses-permission android:name="android.permission.VIBRATE"/>

也就是说,你在自己的AndroidManifest.xml中无需手动重复声明。不过,如果你的应用同时在其他地方手动控制了振动权限,也可以显式声明(与自动添加的声明等价,不会冲突):

<!-- Added permissions --> <uses-permission android:name="android.permission.VIBRATE" />

第三步:配置 iOS

iOS 端安装 npm 包后,需要重新安装 CocoaPods 依赖:

npx pod-install

值得注意的是,VIBRATE权限仅对基于Vibrator的旧式振动 API 有意义。模块在 Android 14.1.0 版本起新增的performAndroidHapticsAsync(调用View.performHapticFeedback不再依赖VIBRATE权限,这一点在 Haptics.ts 的文档注释中被明确强调,详见下文 API 详解。

三、核心 API 详解

模块共暴露 4 个异步方法,均返回Promise<void>,并在原生能力不可用时抛出UnavailabilityError(见 packages/expo-haptics/src/Haptics.ts 中的守卫逻辑)。

3.1notificationAsync(type?):通知级别反馈

用于表达任务结果类语义,如操作成功、警告、失败。默认值为NotificationFeedbackType.Success

import * as Haptics from 'expo-haptics'; // 操作成功后 await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); // 数据校验警告 await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning); // 请求失败 await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);

3.2impactAsync(style?):碰撞冲击反馈

模拟 UI 元素之间发生碰撞的冲击感,常用于按钮按下、卡片被拖动等交互。默认值为ImpactFeedbackStyle.Medium

import * as Haptics from 'expo-haptics'; await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium); await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy); await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Rigid); await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Soft);

3.3selectionAsync():选择变更反馈

用于在用户切换选择项(如滚动选择器、切换 Tab、滑块移动)时给出轻量确认,无参数:

import * as Haptics from 'expo-haptics'; await Haptics.selectionAsync();

3.4performAndroidHapticsAsync(type):Android 专用系统触觉

这是 Android 平台推荐的新式触觉 API(Android 14.1.0 引入,见 CHANGELOG.md)。它直接调用View.performHapticFeedback,效果与 iOS 触觉反馈类似,且不需要VIBRATE权限

import * as Haptics from 'expo-haptics'; // 仅 Android 生效;其他平台直接返回 await Haptics.performAndroidHapticsAsync(Haptics.AndroidHaptics.Confirm);

从 Haptics.ts 的源码可以看到,该函数在非 Android 平台会直接return,不会抛错,因此可以安全地在跨平台代码中调用。

3.5 枚举类型对照表

NotificationFeedbackType(通知反馈)

枚举值说明
Success任务成功完成
Warning任务产生警告
Error任务失败

ImpactFeedbackStyle(冲击反馈强度)

枚举值说明
Light小型、轻量 UI 元素之间的碰撞
Medium中等尺寸 UI 元素之间的碰撞
Heavy大型、重型 UI 元素之间的碰撞
Soft柔软、弹性大的碰撞
Rigid坚硬、弹性小的碰撞

AndroidHaptics(Android 系统预置触觉,共 22 种)

枚举值触发场景
Confirm确认或成功完成用户交互
Reject拒绝或失败
Gesture_Start/Gesture_End手势开始 / 结束(如软键盘)
Toggle_On/Toggle_Off开关切换到开 / 关
Clock_Tick时钟刻度按下
Context_Click上下文点击
Drag_Start拖拽开始(拖拽目标被"拿起")
Keyboard_Tap/Keyboard_Press/Keyboard_Release软键盘按键按下 / 释放
Long_Press长按触发动作
Virtual_Key/Virtual_Key_Release虚拟按键按下 / 释放
No_Haptics不执行任何触觉反馈
Segment_Tick在少量候选项之间切换(如列表项、滑块离散点)
Segment_Frequent_Tick在大量候选项之间快速切换(如时钟分钟刻度),设计为极轻、可高频触发,若设备无法产生足够轻柔的振动则可能不振动
Text_Handle_Move文本选区 / 插入点手柄移动

完整枚举定义见 packages/expo-haptics/src/Haptics.types.ts。

四、源码级原理:三大平台的底层实现

4.1 iOS:基于 UIKit 的三种 Feedback Generator

iOS 实现位于 packages/expo-haptics/ios/HapticsModule.swift,模块名注册为ExpoHaptics。三个异步函数分别对应系统三套触觉生成器:

  • notificationAsyncUINotificationFeedbackGenerator,先调用prepare()预热,再按类型触发notificationOccurred(.success / .warning / .error)
  • impactAsyncUIImpactFeedbackGenerator(style:)prepare()后调用impactOccurred(),其中light / medium / heavy / soft / rigid直接映射到UIImpactFeedbackGenerator.FeedbackStyle的五个枚举值;
  • selectionAsyncUISelectionFeedbackGeneratorprepare()后调用selectionChanged()

三个函数都通过.runOnQueue(.main)强制在主线程执行。这一点非常关键——历史版本中曾因不在主线程调用 Feedback Generator 而导致 iOS 偶发崩溃(该修复记录在 CHANGELOG.md 12.0.1 版本中),因此在自定义原生实现时务必遵循"主线程调用 + 提前 prepare"的规范。

4.2 Android:Vibrator 波形模拟与系统触觉反馈

Android 实现位于 packages/expo-haptics/android/src/main/java/expo/modules/haptics/HapticsModule.kt。

Vibrator 的获取做了版本适配:Android 12(API 31,Build.VERSION_CODES.S)及以上通过VibratorManager.defaultVibrator获取;更早版本则使用旧式Context.VIBRATOR_SERVICE(带@Suppress("DEPRECATION")标注)。

振动波形的生成vibrate私有方法)同样按 API 版本分流:

  • API 26(Android 8.0 /Build.VERSION_CODES.O)及以上:使用VibrationEffect.createWaveform(timings, amplitudes, -1),其中-1表示不重复、只振动一次;
  • 更早版本:退化为vibrator.vibrate(pattern, -1)的旧式纯时长模式。

每种反馈类型都预置了精确的振动参数(时间 ms 与振幅 0–255),位于 packages/expo-haptics/android/src/main/java/expo/modules/haptics/arguments/ 下:

HapticsImpactType.kt定义的冲击反馈波形(timings/amplitudes/ 旧 SDK 兼容 pattern):

类型timings (ms)amplitudes (0–255)
light/soft[0, 50][0, 30]
medium/rigid[0, 43][0, 50]
heavy[0, 60][0, 70]

HapticsNotificationType.kt定义的通知反馈波形:

类型timings (ms)amplitudes (0–255)
success[0, 40, 100, 40][0, 50, 0, 60]
warning[0, 40, 120, 60][0, 40, 0, 60]
error[0, 60, 100, 40, 80, 50][0, 50, 0, 40, 0, 50]

其中timings数组的元素按"振动—停顿"交替解释:如[0, 40, 100, 40]表示立即开始振动 40ms、停顿 100ms、再振动 40ms。非法参数(如字符串拼写错误)会抛出HapticsInvalidArgumentException,其消息会明确列出合法取值。

performHapticsAsync的特殊处理:该方法通过appContext.currentActivity找到内容视图(android.R.id.content)并调用view.performHapticFeedback(...)。由于该方法必须运行在主线程(main-thread affine),而模块默认调度线程上调用会静默无效,因此源码中显式使用了.runOnQueue(Queues.MAIN)——这正是模块 CHANGELOG.md Unpublished 版本中记录的 bug 修复("FixperformAndroidHapticsAsyncdoing nothing by running it on the main queue")的根因与解决方案。

4.3 Web:Web Vibration API 与 iOS Safari 降级

Web 实现位于 packages/expo-haptics/src/ExpoHaptics.web.ts,是理解跨平台降级设计的绝佳样例。

第一步:能力检测isVibrationAvailable()检查navigator.vibrate是否存在;supportsCoarsePointer通过matchMedia('(pointer: coarse)')判断是否为触屏设备。

第二步:振动模式映射vibrationPatterns表把各类反馈映射为振动时长序列:

反馈类型振动模式 (ms)
Success[40, 100, 40]
Warning[50, 100, 50]
Error[60, 100, 60, 100, 60]
Light[40]
Medium[50]
Heavy[60]
Soft[35]
Rigid[45]
selection[50]

第三步:iOS Safari 特判。iOS Safari 不支持navigator.vibrate,但系统会对 switch 开关切换提供原生触感。实现利用这一点:动态创建一个隐藏的<input type="checkbox" switch>元素挂到document.head,通过labelEl.click()触发原生触觉反馈后立即移除(iOSSwitchHaptic()函数,该技巧自 55.0.12 版本加入)。对于Error这类需要多次脉冲的反馈,会以 120ms 间隔连续触发多次,模拟更强烈的提醒。

从调用链可以看出,Web 端与原生端共用同一套 TypeScript API(ExpoHaptics.ts通过requireOptionalNativeModule('ExpoHaptics')按平台解析实现),这也是模块"一套 API、三端一致"体验的架构基础。

五、实践建议与注意事项

  1. Android 上优先使用performAndroidHapticsAsync。模块在 Haptics.ts 中明确给出官方建议:VibratorAPI 并不适合实现精细的触觉反馈,应优先使用performAndroidHapticsAsync,它更接近 iOS 的触觉体验,且免去了VIBRATE权限要求。
  2. 注意impactAsync等旧 API 的跨端差异:同一枚举值在 iOS 上直接映射 UIKit 反馈类型,在 Android 上则被翻译为预设振动波形,在 Web 上被翻译为时长序列——因此"同一种反馈"在三端感知并不完全相同,设计交互时应以真机验证为准。
  3. iOS 主线程约束:如果在自有原生模块中实现触觉,务必在主线程调用 Feedback Generator 并提前prepare(),避免偶发崩溃。
  4. 系统设置感知:触觉反馈的最终表现受系统"触觉/振动"设置影响,代码层面无法绕过;Web 端则需要用户浏览器授予振动能力。
  5. 版本要求:当前仓库中 expo-haptics 版本为 57.0.1(见 packages/expo-haptics/package.json);56.0.0 起最低 iOS/tvOS 版本提升至 16.4、macOS 13.4,集成时请确认工程的最低系统版本满足要求。

六、参考文件索引

  • API 入口与调用链:packages/expo-haptics/src/Haptics.ts
  • 类型与枚举定义:packages/expo-haptics/src/Haptics.types.ts
  • iOS 原生实现:packages/expo-haptics/ios/HapticsModule.swift
  • Android 原生实现:packages/expo-haptics/android/src/main/java/expo/modules/haptics/HapticsModule.kt
  • Android 振动参数:arguments/目录下的 HapticsImpactType.kt、HapticsNotificationType.kt 等
  • Web 实现与降级策略:packages/expo-haptics/src/ExpoHaptics.web.ts
  • Android 权限声明:packages/expo-haptics/android/src/main/AndroidManifest.xml
  • 版本演进记录:packages/expo-haptics/CHANGELOG.md

【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询