ARTICLE DETAIL

资讯详情

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

React Native Gesture Handler 手册手势(Manual Gesture)触控收尾:用 onTouchesUp 完成指针跟踪与手势结束

React Native Gesture Handler 手册手势(Manual Gesture)触控收尾:用 onTouchesUp 完成指针跟踪与手势结束 移动开发UI组件【免费下载链接】react-native-gesture-handlerDeclarative API exposing platform native touch and gesture system to React Native.项目地址https://gitcode.com/gh_mirrors/re/react-native-gesture-handler点击查看免费下载本文是 React Native Gesture HandlerRNGH2.x 手册手势Manual Gestures系列指南的关键收尾篇聚焦于Gesture.Manual()手势中onTouchesUp回调的正确实现如何在手指抬起时隐藏指针标记、如何依据numberOfTouches判断手势是否应当结束以及为什么在基础场景中可以暂不处理onTouchesCancelled。读完本文你将掌握 RNGH 触控事件touch events回调的完整生命周期用法并能把一套跟踪屏幕上所有指针的手势从按下down到移动move再到抬起up完整落地。背景手册手势与触控事件流RNGH 2 引入了社区期待已久的**手册手势Manual Gestures与触控事件Touch Events**能力。与基于识别器自动判断的手势不同Gesture.Manual()不会自动激活而是把每一次触摸的细节——指针按下、移动、抬起、取消——以事件回调的形式交给开发者由你自行决定何时激活、何时结束手势。官方指南以一个跟踪屏幕上所有指针的示例展开完整教程见 Manual gestures 指南共分为 7 个步骤Step 1定义Pointer接口存储指针是否可见visible及其位置x、yStep 2编写PointerElement组件用两个共享值shared value驱动动画样式未激活时显示为蓝色小球、激活后变为红色且放大 1.3 倍Step 3创建承载手势的组件用长度为12的数组保存指针数据12 是 RNGH 单手势最多追踪的触摸数量并渲染到Animated.View中Step 4在onTouchesDown中记录按下指针的位置并置为可见同时当numberOfTouches 2时调用manager.activate()激活手势Step 5在onTouchesMove中持续更新移动指针的位置Step 6本文核心在onTouchesUp中隐藏抬起的手指、并在屏幕上没有任何指针时结束手势Step 7通过onStart/onEnd维护active共享值驱动指针小球的颜色与大小动画。可以看出Step 6 是整个生命周期中最容易出错的一环如果只更新数据而不做状态收尾手势对象将永远停留在激活状态active共享值也无法复位后续手势将无法正确开启。Step 6 核心代码onTouchesUp 的收尾逻辑官方 Step 6 原文 给出的代码片段如下注意代码片段前的...表示在Gesture.Manual()链式调用中继续追加回调const gesture Gesture.Manual() ... .onTouchesUp((e, manager) { for (const touch of e.changedTouches) { trackedPointers[touch.id].value { visible: false, x: touch.x, y: touch.y, }; } if (e.numberOfTouches 0) { manager.end(); } })这段代码承担了三项职责缺一不可遍历changedToucheschangedTouches是本次事件中发生变化的触摸数组。在抬起事件中它包含所有离开屏幕的触摸。通过touch.id定位到trackedPointers数组中的对应槽位该数组长度为 12与 RNGH 最大追踪触摸数一致将visible置为false以隐藏指针小球同时保留其最后位置x、y——这样小球会在原地淡出而非跳回原点视觉上更自然依据numberOfTouches判断是否结束e.numberOfTouches表示当前仍停留在屏幕上的触摸总数。当它为0说明所有手指都已抬起此时应当调用manager.end()结束手势显式管理手势状态与自动识别的手势不同Manual手势不会自行结束必须由开发者在合适的时机调用manager.end()否则手势将停留在激活状态onEnd永远不会触发。将 Step 4、Step 5 与 Step 6 串联起来可以看到完整的状态机触控事件回调数据更新状态管理手指按下onTouchesDown写入位置、visible: truenumberOfTouches 2时manager.activate()手指移动onTouchesMove更新移动指针的位置无手指抬起onTouchesUpvisible: false保留最后位置numberOfTouches 0时manager.end()意外取消onTouchesCancelled清理取消指针的数据示例中省略生产环境建议处理触控事件回调的源码级原理在 RNGH 源码中所有触控事件回调都定义在 gesture.ts 的Gesture基类上包括onTouchesDown、onTouchesMove、onTouchesUp与onTouchesCancelled四个方法每个方法内部都会执行两个关键动作onTouchesUp(callback: TouchEventHandlerType) { this.config.needsPointerData true; this.handlers.onTouchesUp callback; this.handlers.isWorklet[CallbackType.TOUCHES_UP] this.isWorklet(callback); return this; }this.config.needsPointerData true注册任意触控事件回调都会强制开启底层事件系统的指针数据追踪pointer data。也就是说只要你在手势上挂了onTouchesUpRNGH 就会为整个手势周期收集完整的触控流信息事件对象中的changedTouches、numberOfTouches、allTouches等字段才有数据来源this.handlers.isWorklet[...] this.isWorklet(callback)RNGH 检测回调是否为 Reanimated worklet。当与react-native-reanimated配合时onTouchesUp可以写成 worklet直接在 UI 线程更新共享值本示例中trackedPointers[touch.id].value {...}的写法即依赖这一点避免跨线程通信带来的延迟。从 manualGesture.ts 可以看到ManualGesture类继承自ContinousBaseGesture其handlerName为ManualGestureHandler——它本质上是一种连续手势只是把激活与结束的决定权完全交给了manager.activate()/manager.end()调用。为什么本示例不处理 onTouchesCancelled官方指南在 Step 6 的文字说明中特别指出本示例没有处理onTouchesCancelled因为在这样一个基础的用例中并不期望取消事件发生。但同时它给出了明确的工程建议you should clear data about cancelled pointers (most of the time all active ones) when it is called——即一旦收到取消回调应当清理被取消指针大多数情况下是全部活跃指针的数据。取消事件何时出现例如来电打断、系统手势抢占、GestureDetector所在视图被系统 UI 覆盖或者在同一手势树中其他手势赢得识别竞争时底层会派发取消而不是正常的抬起。此时如果只实现了onTouchesUp屏幕上可能残留visible: true的幽灵指针并且manager.end()永远不会被调用。一个生产可用的加固写法如下.onTouchesCancelled((e, manager) { for (const touch of e.changedTouches) { trackedPointers[touch.id].value { visible: false, x: touch.x, y: touch.y }; } manager.end(); })从源码看onTouchesCancelled同样在 gesture.ts 中定义与onTouchesUp走同一套needsPointerData机制二者的收尾逻辑可以完全对称。串联完整示例指针跟踪手势全貌将 Step 1 至 Step 7 的代码合并即可得到一个可运行的指针跟踪示例各片段分别见 Step 1、Step 2、Step 3、Step 4、Step 5、Step 6、Step 7interface Pointer { visible: boolean; x: number; y: number; } function PointerElement(props: { pointer: Animated.SharedValuePointer, active: Animated.SharedValueboolean, }) { const animatedStyle useAnimatedStyle(() ({ transform: [ { translateX: props.pointer.value.x }, { translateY: props.pointer.value.y }, { scale: (props.pointer.value.visible ? 1 : 0) * (props.active.value ? 1.3 : 1) }, ], backgroundColor: props.active.value ? red : blue, })); return Animated.View style{[styles.pointer, animatedStyle]} /; } export default function Example() { const trackedPointers: Animated.SharedValuePointer[] []; const active useSharedValue(false); for (let i 0; i 12; i) { trackedPointers[i] useSharedValuePointer({ visible: false, x: 0, y: 0 }); } const gesture Gesture.Manual() .onTouchesDown((e, manager) { for (const touch of e.changedTouches) { trackedPointers[touch.id].value { visible: true, x: touch.x, y: touch.y }; } if (e.numberOfTouches 2) { manager.activate(); } }) .onTouchesMove((e, _manager) { for (const touch of e.changedTouches) { trackedPointers[touch.id].value { visible: true, x: touch.x, y: touch.y }; } }) .onTouchesUp((e, manager) { for (const touch of e.changedTouches) { trackedPointers[touch.id].value { visible: false, x: touch.x, y: touch.y }; } if (e.numberOfTouches 0) { manager.end(); } }) .onStart(() { active.value true; }) .onEnd(() { active.value false; }); return ( GestureDetector gesture{gesture} Animated.View style{{ flex: 1 }} {trackedPointers.map((pointer, index) ( PointerElement pointer{pointer} active{active} key{index} / ))} /Animated.View /GestureDetector ); }值得注意的细节12 个指针槽位的上限RNGH 单手势最多追踪 12 个触摸因此数组长度取 12。若某个touch.id越界说明底层事件数据超出预期可作为防御性检查点onTouchesUp与onEnd的分工onTouchesUp处理的是触控事件指针抬起onEnd处理的是状态变化手势结束。本例中manager.end()触发onEnd从而将active置为false小球恢复蓝色并缩小。二者职责不同不可相互替代worklet 与共享值所有触控回调内部直接写入trackedPointers与active等共享值配合 Reanimated 的useAnimatedStyle驱动动画整个指针渲染链路可以全程运行在 UI 线程。进阶触控事件与 manualActivation 的配合官方指南在 index.md 的Modifying existing gestures一节进一步说明触控事件不仅限于Gesture.Manual()任何手势都可以通过onTouchesDown/onTouchesMove/onTouchesUp提取比基础事件更详细的信息例如手指坐标、触摸数量而无须为了获得这些信息而用Manual从零重写 pinch、rotation 等已经内置的手势。同时所有连续手势都支持manualActivation修饰符开启后手势不会自动激活激活时机完全由开发者控制。源码层面manualActivation定义在 gesture.tsmanualActivation(manualActivation: boolean) { this.config.manualActivation manualActivation; return this; }官方给出的典型场景是拖拽前长按drag after long press给PanGesture设置manualActivation{true}再用StateManager在用户过早拖动时让手势 fail只有按住足够时长后才允许进入激活状态。这与本文Gesture.Manual()中通过manager.activate()/manager.end()手动驱动状态机的思路一脉相承——区别在于Manual从零构建手势而manualActivation是在内置手势上接管激活权。版本演进提示Gesture.Manual() 的现状需要说明的是本指南基于version-2.x 文档见 version-2.x 文档目录。而在当前仓库的最新源码中Gesture.Manual()工厂方法与ManualGesture类均已被标记为deprecated在 gestureObjects.ts 中可以看到注释Gesture.Manual() is deprecated and will be removed in the future. Please use useManualGesture insteadmanualGesture.ts 顶部也有同样的弃用提示。也就是说在新版本 API 中官方推荐使用useManualGestureHook 替代Gesture.Manual()但本文讲解的onTouchesUp等触控事件回调的语义、changedTouches/numberOfTouches事件字段的含义以及手动收尾、手动结束的核心思想在两个 API 世代中是延续一致的。阅读旧代码、迁移旧项目时这套逻辑依然完全适用。小结onTouchesUp是手册手势生命周期中负责收尾的关键一环它既要通过changedTouches精确隐藏被抬起的手指标记又要通过numberOfTouches 0的判断决定是否调用manager.end()结束手势。结合onTouchesDown的激活判定、onTouchesMove的位置更新以及生产环境中应当补充的onTouchesCancelled清理逻辑你就能完整掌控 RNGH 触控事件的来龙去脉进而用Gesture.Manual()或新版本的useManualGesture实现任何过去无法用现成手势完成的交互。赞分享移动开发UI组件【免费下载链接】react-native-gesture-handlerDeclarative 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 手动手势Manual Gesture之 onTouchesUp多指抬起检测与手势状态收尾实战react native gesture handler 手动手势Manual Gesture之 onTouchesUp多指抬起检测与手势状态收尾实战 本移动开发UI组件react-native-gesture-handler 手动手势Manual Gesture实战构建多点触控指针追踪容器组件react native gesture handler 手动手势Manual Gesture实战构建多点触控指针追踪容器组件 本指南聚焦 react n移动开发UI组件react-native-gesture-handler 手动手势Manual Gesture实战从 Pointer 数据模型到多点触控追踪器react native gesture handler 手动手势Manual Gesture实战从 Pointer 数据模型到多点触控追踪器 本指南以移动开发UI组件上一篇IronClaw Reborn 集成测试框架设计解析在真实 LLM 装饰器链底部做单点 Mock并用真实 SQL 覆盖持久化下一篇date-fns Gujaratigu区域设置详解format / parse / formatDistance 快照全解读创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表