ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

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

Expo Haptics 触觉反馈模块实战指南:iOS 触觉引擎、Android 振动与 Web Vibration 的统一封装 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/expoexpo-haptics 是 Expo 官方提供的触觉反馈模块用于在 React Native 应用中触发系统级物理反馈iOS 使用系统触觉引擎Haptics EngineAndroid 使用振动效果Web 端使用 Web Vibration API。本指南将围绕该模块的安装配置、完整 API 用法与各平台底层实现原理展开帮助你在一行代码内为用户提供高质量的触感交互体验并理解原生层Swift / Kotlin与 Web 层的具体工作方式。一、模块概览一套 API三种平台实现从 packages/expo-haptics/package.json 的定义可以看出本模块的核心定位是Provides access to the systems haptics engine on iOS, vibration effects on Android, and Web Vibration API on web即iOS调用系统触觉引擎UINotificationFeedbackGenerator、UIImpactFeedbackGenerator、UISelectionFeedbackGenerator提供细腻、真实的触感Android通过系统Vibrator产生振动波形或直接调用View.performHapticFeedback触发系统预置触觉反馈Web基于 Web Vibration APInavigator.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 托管ManagedExpo 项目对于托管项目直接使用 Expo CLI 的版本对齐命令安装即可它会自动选择与当前 SDK 匹配的版本npx expo install expo-haptics安装后无需任何手动配置模块即可开箱即用。2.2 纯 React NativeBare项目纯 React Native 项目需要先确保已经安装并配置好expo包然后按以下步骤操作第一步添加 npm 依赖npx expo install expo-haptics第二步配置 Android该模块需要控制设备振动的权限权限会在构建时自动添加。查看模块自带的 packages/expo-haptics/android/src/main/AndroidManifest.xml可以看到它声明了uses-permission android:nameandroid.permission.VIBRATE/也就是说你在自己的AndroidManifest.xml中无需手动重复声明。不过如果你的应用同时在其他地方手动控制了振动权限也可以显式声明与自动添加的声明等价不会冲突!-- Added permissions -- uses-permission android:nameandroid.permission.VIBRATE /第三步配置 iOSiOS 端安装 npm 包后需要重新安装 CocoaPods 依赖npx pod-install值得注意的是VIBRATE权限仅对基于Vibrator的旧式振动 API 有意义。模块在 Android 14.1.0 版本起新增的performAndroidHapticsAsync调用View.performHapticFeedback不再依赖VIBRATE权限这一点在 Haptics.ts 的文档注释中被明确强调详见下文 API 详解。三、核心 API 详解模块共暴露 4 个异步方法均返回Promisevoid并在原生能力不可用时抛出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 平台推荐的新式触觉 APIAndroid 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坚硬、弹性小的碰撞AndroidHapticsAndroid 系统预置触觉共 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 GeneratoriOS 实现位于 packages/expo-haptics/ios/HapticsModule.swift模块名注册为ExpoHaptics。三个异步函数分别对应系统三套触觉生成器notificationAsync→UINotificationFeedbackGenerator先调用prepare()预热再按类型触发notificationOccurred(.success / .warning / .error)impactAsync→UIImpactFeedbackGenerator(style:)prepare()后调用impactOccurred()其中light / medium / heavy / soft / rigid直接映射到UIImpactFeedbackGenerator.FeedbackStyle的五个枚举值selectionAsync→UISelectionFeedbackGeneratorprepare()后调用selectionChanged()。三个函数都通过.runOnQueue(.main)强制在主线程执行。这一点非常关键——历史版本中曾因不在主线程调用 Feedback Generator 而导致 iOS 偶发崩溃该修复记录在 CHANGELOG.md 12.0.1 版本中因此在自定义原生实现时务必遵循主线程调用 提前 prepare的规范。4.2 AndroidVibrator 波形模拟与系统触觉反馈Android 实现位于 packages/expo-haptics/android/src/main/java/expo/modules/haptics/HapticsModule.kt。Vibrator 的获取做了版本适配Android 12API 31Build.VERSION_CODES.S及以上通过VibratorManager.defaultVibrator获取更早版本则使用旧式Context.VIBRATOR_SERVICE带Suppress(DEPRECATION)标注。振动波形的生成vibrate私有方法同样按 API 版本分流API 26Android 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 WebWeb 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 typecheckbox switch元素挂到document.head通过labelEl.click()触发原生触觉反馈后立即移除iOSSwitchHaptic()函数该技巧自 55.0.12 版本加入。对于Error这类需要多次脉冲的反馈会以 120ms 间隔连续触发多次模拟更强烈的提醒。从调用链可以看出Web 端与原生端共用同一套 TypeScript APIExpoHaptics.ts通过requireOptionalNativeModule(ExpoHaptics)按平台解析实现这也是模块一套 API、三端一致体验的架构基础。五、实践建议与注意事项Android 上优先使用performAndroidHapticsAsync。模块在 Haptics.ts 中明确给出官方建议VibratorAPI 并不适合实现精细的触觉反馈应优先使用performAndroidHapticsAsync它更接近 iOS 的触觉体验且免去了VIBRATE权限要求。注意impactAsync等旧 API 的跨端差异同一枚举值在 iOS 上直接映射 UIKit 反馈类型在 Android 上则被翻译为预设振动波形在 Web 上被翻译为时长序列——因此同一种反馈在三端感知并不完全相同设计交互时应以真机验证为准。iOS 主线程约束如果在自有原生模块中实现触觉务必在主线程调用 Feedback Generator 并提前prepare()避免偶发崩溃。系统设置感知触觉反馈的最终表现受系统触觉/振动设置影响代码层面无法绕过Web 端则需要用户浏览器授予振动能力。版本要求当前仓库中 expo-haptics 版本为 57.0.1见 packages/expo-haptics/package.json56.0.0 起最低 iOS/tvOS 版本提升至 16.4、macOS 13.4集成时请确认工程的最低系统版本满足要求。六、参考文件索引API 入口与调用链packages/expo-haptics/src/Haptics.ts类型与枚举定义packages/expo-haptics/src/Haptics.types.tsiOS 原生实现packages/expo-haptics/ios/HapticsModule.swiftAndroid 原生实现packages/expo-haptics/android/src/main/java/expo/modules/haptics/HapticsModule.ktAndroid 振动参数arguments/目录下的 HapticsImpactType.kt、HapticsNotificationType.kt 等Web 实现与降级策略packages/expo-haptics/src/ExpoHaptics.web.tsAndroid 权限声明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),仅供参考
返回列表