☰
react-native-gesture-handler 通用手势回调全解析:onBegin / onStart / onEnd / onFinalize 与触摸事件回调实战指南
2026/10/7 1:51:01 网站建设 项目流程
  • 移动开发
  • UI组件

【免费下载链接】react-native-gesture-handler

Declarative API exposing platform native touch and gesture system to React Native.

项目地址:https://gitcode.com/gh_mirrors/re/react-native-gesture-handler
点击查看免费下载

导读

本文聚焦 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,直观呈现两套回调与状态机的对应关系。


七、最佳实践与注意事项

  1. 用useMemo包裹手势配置:如 gesture.md 所建议,手势对象每次渲染被重建会增加底层更新开销;配合useMemo依赖数组可显著减少无谓重建(源码中gestureId机制正是用于检测配置是否变化,见 gesture.ts)。
  2. 清理逻辑放onFinalize:它覆盖"识别成功结束"与"识别失败"两种结局,是重置动画状态、释放资源的统一出口;onEnd只在曾进入 ACTIVE 时触发,适合"成功收尾"类逻辑。
  3. 多指追踪用id:changedTouches/allTouches的元素顺序可能变化,务必以触摸项id为准进行跨事件追踪。
  4. 形变场景用绝对坐标:当视图本身被手势移动/缩放时,x/y会随视图变换失真,此时应改用absoluteX/absoluteY。
  5. 触摸回调有性能成本:onTouchesMove等回调触发频率高,内部需要开启needsPointerData装配指针数据;若不需要原始触摸信息,优先使用onUpdate/onChange等状态级回调。
  6. 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.

项目地址:https://gitcode.com/gh_mirrors/re/react-native-gesture-handler
点击查看免费下载
上一篇:HubSpot/select 项目推荐
下一篇:10x效率提升:GitHub通知在Gmail中一键直达的终极方案

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

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

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

立即咨询