- 移动开发
- UI组件
【免费下载链接】react-native-gesture-handler
Declarative API exposing platform native touch and gesture system to React Native.
导读
本文聚焦 react-native-gesture-handler v2 版 API 中所有手势共享的回调体系,完整讲解onBegin、onStart、onEnd、onFinalize四个状态变化回调,以及onTouchesDown、onTouchesMove、onTouchesUp、onTouchesCancelled四个原始触摸事件回调的触发时机、事件数据与底层实现。读完本文,你将能够精确把握手势从「开始接收触摸」到「识别成功」再到「结束/失败」的完整生命周期,并能在自己的GestureDetector中正确编排这些回调实现拖拽、缩放、点击反馈等交互。文中所有源码依据均来自本仓库 packages/react-native-gesture-handler/src/handlers/gestures/gesture.ts 与 v2 版文档。
一、回调全景:两个维度看透手势
react-native-gesture-handler v2 的手势对象(如Gesture.Tap()、Gesture.Pan())提供了两类通用回调:
| 回调类别 | 回调方法 | 触发维度 |
|---|---|---|
| 状态变化回调 | onBegin/onStart/onEnd/onFinalize | 手势在状态机中的状态迁移 |
| 触摸事件回调 | onTouchesDown/onTouchesMove/onTouchesUp/onTouchesCancelled | 屏幕上的原始触摸流 |
要理解第一类回调,必须先了解手势状态机。在 react-native-gesture-handler 中,每个手势都被视为一个状态机,共有六种状态:UNDETERMINED(初始状态)、FAILED、BEGAN、CANCELLED、ACTIVE、END。最典型的状态流转是UNDETERMINED → BEGAN → ACTIVE → END → UNDETERMINED(参见 states-events.mdx)。上述回调正是贴着这些状态迁移点触发的。
二、状态变化回调:手势生命周期四件套
2.1onBegin(callback)—— 开始接收触摸
设置在手势处理器开始接收触摸时被调用的回调。在onBegin触发的时刻,处理器尚处于BEGAN状态,还未进入 active 状态,我们也还无法确定它最终是否会识别该手势。它对应状态机中BEGAN状态的进入。
2.2onStart(callback)—— 手势被识别、进入 ACTIVE
设置在手势被处理器识别成功并迁移到 active 状态时被调用的回调。这是"识别成功"的确认信号:此后onUpdate(连续手势)会开始密集触发,直到手势结束。
2.3onEnd(callback)—— 手势结束
设置在手势被识别后完成结束时被调用的回调。它只会在处理器此前处于 active 状态时被调用。
回调签名为(event, success) => void,其中:
event:状态变化事件(GestureStateChangeEvent),携带state与oldState;success: boolean:手势以END状态结束时为true;若迁移到FAILED或CANCELLED则为false(详见 states-events.mdx)。
典型场景:手指抬起后执行"松手动画"或提交一次拖拽结果。
2.4onFinalize(callback)—— 收尾兜底
设置在手势处理器完成对该手势的处理时被调用的回调——无论手势是被识别后正常结束,还是识别失败都会触发。其签名同样为(event, success) => void,success的取值规则与onEnd一致。
两者的关系值得强调:如果手势是从ACTIVE状态迁移出去的,onFinalize会在onEnd之后被调用(见 states-events.mdx)。因此onFinalize是"无论成功失败都必须做一次清理"的最佳位置——例如重置动画值、恢复组件状态。
从源码看,这四个回调被统一声明在
HandlerCallbacks类型中(gesture.ts),且onEnd、onFinalize的回调签名都接收(event, success)两个参数;对应实现方法onBegin()、onStart()、onEnd()、onFinalize()通过this.handlers.onXxx = callback完成注册并返回this支持链式调用(同文件 L182-L229)。
三、触摸事件回调:原始触摸流四通道
第二类回调在每一次手指(指针)触摸屏幕的原始事件上触发,与手势是否被识别无关。它们接收GestureTouchEvent事件对象(类型定义见 gesture.ts 中的TouchEventHandlerType)。
| 回调 | 触发时机 |
|---|---|
onTouchesDown | 每次有手指放到屏幕上时 |
onTouchesMove | 每次有手指在屏幕上移动时 |
onTouchesUp | 每次有手指从屏幕抬起时 |
onTouchesCancelled | 每次有手指停止被追踪时(例如手势结束时) |
事件对象包含如下属性(详见 touch-events.md):
eventType:当前事件类型——手指按下、移动、抬起或取消;changedTouches:仅包含本次事件受影响触摸的数组(按下/移动/抬起/取消的那批手指);allTouches:当前所有活跃触摸的数组;numberOfTouches:当前活跃触摸的数量。
其中每个触摸项(PointerData)还带有:
id:触摸的唯一标识,在追踪期间保持不变,可用于跨事件追踪同一根手指;x/y:触摸相对于GestureDetector所连接视图的坐标(point 单位);absoluteX/absoluteY:触摸相对于窗口的坐标,当视图因手势发生形变时建议用绝对坐标代替相对坐标。
注意:不要依赖touches数组中元素的排列顺序(它可能在手势过程中变化),请用id属性追踪单根手指。
四、连续手势专属回调:onUpdate 与 onChange
onUpdate/onChange不在本文主文档范围内,但它们是同一_shared目录下、连续手势(Pan、Pinch、Rotation 等)最常用的补充回调,一并说明(依据 base-continuous-gesture-callbacks.md):
onUpdate(callback):手势处于active 状态期间每次收到更新时触发;onChange(callback):同样在 active 期间每次更新时触发,但事件中携带的是相对上一次事件的增量值(例如translationX的增量)。
它们在源码中定义于ContinousBaseGesture基类(gesture.ts),同时该基类还提供了manualActivation(value)配置:设为true后手势不会自行激活,需配合GestureStateManager手动控制状态。
五、源码级实现原理
5.1 回调注册与 worklet 判定
从 gesture.ts 可以看到,每个注册方法都会调用isWorklet(callback)检测回调是否携带__workletHash属性(reanimated worklet 的标识),并把结果写入this.handlers.isWorklet[]数组的对应位(CallbackType枚举位)。这一机制与runOnJS配置、shouldUseReanimated决定(同文件 L427-L436)共同决定了回调最终运行在 UI 线程还是 JS 线程:
- 安装了
react-native-reanimated时,worklet 回调默认自动在 UI 线程运行; - 若
runOnJS(true)被显式设置,则所有回调强制在 JS 线程运行; - 当任一回调不是 worklet 或开启远程调试时,会退回到 JS 线程执行。
5.2onTouches*与needsPointerData
四个onTouches*方法(gesture.ts)除了注册回调外,还会把this.config.needsPointerData = true——这是告诉底层需要额外装配指针数据的开关,也是这些回调能拿到changedTouches/allTouches等原始触摸数据的前提。
5.3 与状态事件模型的对应
v2 的事件模型包含三类事件:StateChangeEvent(状态迁移时发送,携带state与oldState)、GestureEvent(手势更新时发送)与PointerEvent(原始触摸事件)。onBegin/onStart/onEnd/onFinalize分别映射BEGAN、ACTIVE、ACTIVE→END/FAILED/CANCELLED、END/FAILED/CANCELLED的迁移点;onTouchesDown/Move/Up/Cancelled则对应PointerEvent的批处理分发(多指事件会被合并批量投递),参见 states-events.mdx。
六、实战示例:完整可运行代码
下面用一个Pan+Tap组合演示上述回调在真实组件中的用法(参考 gesture.md 中的GestureDetector用法):
import React from 'react'; import { StyleSheet, View, Text } from 'react-native'; import { Gesture, GestureDetector } from 'react-native-gesture-handler'; function GestureCallbacksDemo() { // 用 useMemo 包裹手势配置,减少底层更新开销 const pan = React.useMemo( () => Gesture.Pan() .onBegin(() => { console.log('onBegin: 开始接收触摸(BEGAN 状态,尚未识别)'); }) .onStart(() => { console.log('onStart: 手势被识别,进入 ACTIVE'); }) .onUpdate((event) => { console.log('onUpdate: translation =', event.translationX, event.translationY); }) .onEnd((event, success) => { console.log(`onEnd: 结束, success = ${success}`); }) .onFinalize((event, success) => { console.log(`onFinalize: 收尾, success = ${success}`); }) .onTouchesDown((event) => { console.log('onTouchesDown:', event.changedTouches.length, '根手指按下'); }) .onTouchesMove((event) => { const touch = event.changedTouches[0]; if (touch) { console.log('onTouchesMove:', touch.absoluteX, touch.absoluteY); } }) .onTouchesUp((event) => { console.log('onTouchesUp: 手指抬起, 剩余活跃 =', event.numberOfTouches); }) .onTouchesCancelled((event) => { console.log('onTouchesCancelled: 触摸被系统取消追踪'); }), [] ); return ( <GestureDetector gesture={pan}> <View style={styles.box}> <Text style={styles.text}>拖拽我</Text> </View> </GestureDetector> ); } const styles = StyleSheet.create({ box: { width: 160, height: 160, borderRadius: 16, backgroundColor: '#7c3aed', justifyContent: 'center', alignItems: 'center' }, text: { color: '#fff', fontSize: 18 }, }); export default GestureCallbacksDemo;运行这段代码并拖拽方块,控制台会依次输出onTouchesDown → onBegin → onStart → onUpdate… → onTouchesUp → onEnd → onFinalize,直观呈现两套回调与状态机的对应关系。
七、最佳实践与注意事项
- 用
useMemo包裹手势配置:如 gesture.md 所建议,手势对象每次渲染被重建会增加底层更新开销;配合useMemo依赖数组可显著减少无谓重建(源码中gestureId机制正是用于检测配置是否变化,见 gesture.ts)。 - 清理逻辑放
onFinalize:它覆盖"识别成功结束"与"识别失败"两种结局,是重置动画状态、释放资源的统一出口;onEnd只在曾进入 ACTIVE 时触发,适合"成功收尾"类逻辑。 - 多指追踪用
id:changedTouches/allTouches的元素顺序可能变化,务必以触摸项id为准进行跨事件追踪。 - 形变场景用绝对坐标:当视图本身被手势移动/缩放时,
x/y会随视图变换失真,此时应改用absoluteX/absoluteY。 - 触摸回调有性能成本:
onTouchesMove等回调触发频率高,内部需要开启needsPointerData装配指针数据;若不需要原始触摸信息,优先使用onUpdate/onChange等状态级回调。 runOnJS与 worklet:UI 线程回调可避免频繁跨线程通信,但如需在回调中调用 JS 侧状态(如setState、导航),需明确runOnJS(true)或依赖 reanimated 的runOnJS机制(详见本文 5.1 节)。
参考文档与源码索引
- 主文档:base-gesture-callbacks.md
- 状态机与事件模型:states-events.mdx
- 触摸事件数据:touch-events.md
- 连续手势回调:base-continuous-gesture-callbacks.md
- 手势事件公共数据:base-gesture-event-data.md
- 核心实现:gesture.ts
- 移动开发
- UI组件
【免费下载链接】react-native-gesture-handler
Declarative API exposing platform native touch and gesture system to React Native.
相关推荐
OpenClaw 会话管理详解:消息路由、DM 隔离与完整会话生命周期
OpenClaw 会话管理详解:消息路由、DM 隔离与完整会话生命周期 OpenClaw 将每一条入站消息按来源(私聊、群聊、定时任务、Webhook 等)路由
移动开发UI组件uni-app x 中 tap-gesture-handler 点击手势组件:属性、回调与实战指南
uni app x 中 tap gesture handler 点击手势组件:属性、回调与实战指南 本文基于 uni app 开源仓库中的组件文档 docs/c
示例工程前端移动开发跨平台react-native-gesture-handler 手势事件公共属性全解析:state、numberOfPointers 与 pointerType
react native gesture handler 手势事件公共属性全解析:state、numberOfPointers 与 pointerType 本指
移动开发UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考