HarmonyOS APP开发---"随手画"白板涂鸦App,需要用到这个库
做一个白板涂鸦 App,手指画线要跟手、双指缩放画布、长按选区------RN 自带 PanResponder 在 JS 线程处理手势,画线总是慢半拍。
@react-native-ohos/react-native-gesture-handler在原生层识别手势,零延迟跟手。
📦 仓库地址:gitcode.com/CPF-RN/rntp... | 安装:npm install @react-native-ohos/react-native-gesture-handler
写在前面
"随手画"是一个简易白板涂鸦 App,核心交互全靠手势:
- 单指拖拽:画笔在画布上画线
- 双指捏合:缩放画布,方便看细节或看全局
- 双指拖拽:平移画布
- 长按:选中已有笔画,可移动或删除
这些手势如果用 RN 自带的 PanResponder,问题很明显:手势事件走 JS Bridge 中转,画线延迟明显,双指操作更是灾难。而且画线手势和画布平移手势会冲突------想画线结果把画布拖走了。
react-native-gesture-handler 把手势识别下沉到原生层 ,在原生线程完成状态机管理,JS 只接收最终结果------零延迟跟手 ,还能用 Gesture.Exclusive / Gesture.Simultaneous 精确控制手势冲突。
这篇文章聊什么
- 画线手势------单指 Pan 驱动 SVG 路径绘制
- 缩放+平移------双指 Pinch + Pan 同时识别
- 手势冲突------Gesture.Exclusive 让画线和拖拽互不干扰
第一步:安装与根组件包裹
bash
npm install @react-native-ohos/react-native-gesture-handler
tsx
import { GestureHandlerRootView } from '@react-native-ohos/react-native-gesture-handler'
export default function App() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<Whiteboard />
</GestureHandlerRootView>
)
}
第二步:单指画线手势
用 Gesture.Pan() 驱动画笔轨迹,配合 react-native-svg 的 Path 渲染:
tsx
import { GestureDetector, Gesture } from '@react-native-ohos/react-native-gesture-handler'
import { View } from 'react-native'
import { Path, Svg } from 'react-native-svg'
import { useSharedValue } from 'react-native-reanimated'
function Whiteboard() {
const paths = useSharedValue<Array<string>>([])
const currentPath = useSharedValue('')
// 单指画线手势
const drawGesture = Gesture.Pan()
.minPointers(1)
.maxPointers(1) // 只识别单指
.onBegin((e) => {
currentPath.value = `M ${e.x} ${e.y}`
})
.onUpdate((e) => {
// 追加路径点
currentPath.value += ` L ${e.x} ${e.y}`
})
.onEnd(() => {
// 画完一条线,存入路径列表
paths.value = [...paths.value, currentPath.value]
currentPath.value = ''
})
return (
<GestureDetector gesture={drawGesture}>
<View style={{ flex: 1, backgroundColor: '#fff' }}>
<Svg style={{ flex: 1 }}>
{/* 已完成的笔画 */}
{paths.value.map((d, i) => (
<Path key={i} d={d} stroke="#333" strokeWidth={3} fill="none" />
))}
{/* 当前正在画的笔画 */}
{currentPath.value ? (
<Path d={currentPath.value} stroke="#4ECDC4" strokeWidth={3} fill="none" />
) : null}
</Svg>
</View>
</GestureDetector>
)
}
手势在原生层识别,onUpdate 实时回调坐标------画线跟手零延迟,比 PanReceiver 快一个量级。
第三步:双指缩放 + 平移(同时识别)
双指操作画布:捏合缩放 + 拖拽平移,两个手势要同时识别:
tsx
// 缩放手势
const pinchGesture = Gesture.Pinch()
.onUpdate((e) => {
scale.value = e.scale
})
// 双指平移手势
const panGesture = Gesture.Pan()
.minPointers(2) // 至少两指
.onUpdate((e) => {
translateX.value = e.translationX
translateY.value = e.translationY
})
// 关键:Simultaneous 让缩放和平移同时生效
const canvasGesture = Gesture.Simultaneous(pinchGesture, panGesture)
Gesture.Simultaneous 是核心------缩放和平移互不阻塞,用户可以一边捏合缩放一边拖拽平移画布,体验和原生画板一样流畅。
第四步:手势冲突------画线 vs 画布拖拽
单指画线 vs 双指拖拽,用 Gesture.Exclusive 确保互斥:
tsx
// 组合:画线(单指)和画布操作(双指)互斥
const combinedGesture = Gesture.Exclusive(drawGesture, canvasGesture)
<GestureDetector gesture={combinedGesture}>
<View style={{ flex: 1, backgroundColor: '#fff' }}>
{/* 画布内容 */}
</View>
</GestureDetector>
Exclusive 让手势按优先级判断------单指触发画线,双指触发画布操作,不会出现"想画线结果把画布拖走"的冲突。
为什么"随手画"选了 Gesture Handler?
| 需求 | RN PanResponder | @react-native-ohos/react-native-gesture-handler |
|---|---|---|
| 手势延迟 | JS 线程,慢半拍 | ✅ 原生层,零延迟 |
| 多指手势 | ❌ 难处理 | ✅ Pan/Pinch 原生支持 |
| 手势同时 | ❌ | ✅ Simultaneous |
| 手势互斥 | ❌ 手动判断 | ✅ Exclusive |
| 与 SVG 联动 | 手动 | ✅ 原生回调驱动 |
总结
"随手画"这个场景里,@react-native-ohos/react-native-gesture-handler 解决了三件事:
- 零延迟画线 ------Pan 手势在原生层识别,
onUpdate实时驱动 SVG Path - 双指缩放+平移 ------
Simultaneous让 Pinch 和 Pan 同时生效 - 手势不冲突 ------
Exclusive让单指画线和双指操作互斥,各管各的
如果你也在做绘画、地图、卡片交互等需要复杂手势的 RN 鸿蒙应用,react-native-gesture-handler 是手势处理的基石。