在 React Native Gesture Handler 的发展历程中,我们经历了从 JSX 组件形式到指令式 API 的演进。本文将深入对比这两种编程模式,重点分析新版指令式手势的优势和使用方法。
两种编程模式的演进
JSX 组件形式(传统方式)
JSX 形式是 Gesture Handler 早期的实现方式,通过包装组件来实现手势识别:
jsx
// 传统 JSX 形式
<TapGestureHandler onHandlerStateChange={handleTap}>
<View style={styles.box}>
<Text>Tap me</Text>
</View>
</TapGestureHandler>
指令式 API(新版推荐)
指令式 API 是 Gesture Handler 2.0+ 引入的新特性,提供了更灵活的手势控制:
jsx
// 新版指令式 API
const tapGesture = Gesture.Tap()
.onStart(() => {
console.log('Tap started');
})
.onEnd(() => {
console.log('Tap ended');
});
return (
<GestureDetector gesture={tapGesture}>
<View style={styles.box}>
<Text>Tap me</Text>
</View>
</GestureDetector>
);
手势状态变化的对比
JSX 形式的状态处理
在 JSX 形式中,我们需要手动处理手势状态:
jsx
import { PanGestureHandler, State } from 'react-native-gesture-handler';
const PanExample = () => {
const handlePan = (event) => {
const { state, translationX, translationY } = event.nativeEvent;
switch (state) {
case State.BEGAN:
console.log('Pan began');
break;
case State.ACTIVE:
console.log('Pan active:', translationX, translationY);
break;
case State.END:
console.log('Pan ended');
break;
case State.CANCELLED:
console.log('Pan cancelled');
break;
}
};
return (
<PanGestureHandler onHandlerStateChange={handlePan}>
<View style={styles.draggable} />
</PanGestureHandler>
);
};
指令式 API 的状态处理
指令式 API 提供了更直观的状态回调:
jsx
import { Gesture } from 'react-native-gesture-handler';
const PanExample = () => {
const panGesture = Gesture.Pan()
.onBegin(() => {
console.log('Pan began');
})
.onStart(() => {
console.log('Pan started');
})
.onUpdate((event) => {
console.log('Pan updating:', event.translationX, event.translationY);
})
.onEnd(() => {
console.log('Pan ended');
})
.onFinalize(() => {
console.log('Pan finalized');
});
return (
<GestureDetector gesture={panGesture}>
<View style={styles.draggable} />
</GestureDetector>
);
};
多个手势处理的详细对比
JSX 形式的多手势处理
在 JSX 形式中,手势关系需要通过 ref 和属性来管理:
jsx
import React, { useRef } from 'react';
import {
TapGestureHandler,
LongPressGestureHandler,
State,
} from 'react-native-gesture-handler';
const MultiGestureJSX = () => {
const doubleTapRef = useRef(null);
return (
<LongPressGestureHandler
minDurationMs={800}
onHandlerStateChange={(event) => {
if (event.nativeEvent.state === State.ACTIVE) {
console.log('Long press detected');
}
}}
>
<View>
<TapGestureHandler
onHandlerStateChange={(event) => {
if (event.nativeEvent.state === State.ACTIVE) {
console.log('Single tap detected');
}
}}
waitFor={doubleTapRef}
>
<View>
<TapGestureHandler
ref={doubleTapRef}
onHandlerStateChange={(event) => {
if (event.nativeEvent.state === State.ACTIVE) {
console.log('Double tap detected');
}
}}
numberOfTaps={2}
>
<View style={styles.multiGestureBox}>
<Text>Tap, Double Tap, or Long Press</Text>
</View>
</TapGestureHandler>
</View>
</TapGestureHandler>
</View>
</LongPressGestureHandler>
);
};
指令式 API 的多手势处理
指令式 API 使用组合器(composer)来管理手势关系:
jsx
import { Gesture } from 'react-native-gesture-handler';
const MultiGestureImperative = () => {
// 定义单个手势
const singleTap = Gesture.Tap()
.maxDuration(250)
.onStart(() => {
console.log('Single tap');
});
const doubleTap = Gesture.Tap()
.maxDuration(250)
.numberOfTaps(2)
.onStart(() => {
console.log('Double tap!');
});
const longPress = Gesture.LongPress()
.minDuration(800)
.onStart(() => {
console.log('Long press!');
});
// 使用组合器管理手势关系
const composed = Gesture.Race(doubleTap, Gesture.Simultaneous(singleTap, longPress));
return (
<GestureDetector gesture={composed}>
<View style={styles.multiGestureBox}>
<Text>Tap, Double Tap, or Long Press</Text>
</View>
</GestureDetector>
);
};
手势组合器的详细说明
主要组合器类型
-
Gesture.Race(gesture1, gesture2, ...)
- 竞争关系,第一个触发的手势获胜
- 其他手势会被取消
-
Gesture.Simultaneous(gesture1, gesture2, ...)
- 同时识别多个手势
- 所有手势可以同时处于激活状态
-
Gesture.Exclusive(gesture1, gesture2, ...)
- 互斥关系,一次只能有一个手势激活
- 类似 Race,但有更严格的控制
复杂手势组合示例
jsx
const ComplexGestureExample = () => {
const pan = Gesture.Pan()
.onUpdate((event) => {
console.log('Pan update:', event.translationX, event.translationY);
});
const pinch = Gesture.Pinch()
.onUpdate((event) => {
console.log('Pinch scale:', event.scale);
});
const rotation = Gesture.Rotation()
.onUpdate((event) => {
console.log('Rotation:', event.rotation);
});
// 同时支持拖拽、缩放、旋转
const simultaneousGestures = Gesture.Simultaneous(pan, pinch, rotation);
// 或者:拖拽和缩放/旋转互斥
const exclusiveGestures = Gesture.Exclusive(
pan,
Gesture.Simultaneous(pinch, rotation)
);
return (
<GestureDetector gesture={simultaneousGestures}>
<View style={styles.interactiveBox}>
<Text>Drag, Pinch, or Rotate</Text>
</View>
</GestureDetector>
);
};
性能对比和最佳实践
性能优势
- 更少的内存占用:指令式 API 减少了组件嵌套层级
- 更好的类型安全:TypeScript 支持更完善
- 更清晰的代码结构:手势逻辑集中管理
迁移建议
jsx
// 从 JSX 形式迁移到指令式 API 的示例
// 之前:JSX 形式
<TapGestureHandler
onHandlerStateChange={handleTap}
numberOfTaps={2}
>
<View style={styles.target} />
</TapGestureHandler>
// 之后:指令式 API
const doubleTap = Gesture.Tap()
.numberOfTaps(2)
.onStart(handleTap);
<GestureDetector gesture={doubleTap}>
<View style={styles.target} />
</GestureDetector>
实际应用场景
1. 图片查看器(缩放 + 平移)
jsx
const ImageViewer = ({ imageUrl }) => {
const scale = useSharedValue(1);
const savedScale = useSharedValue(1);
const translateX = useSharedValue(0);
const translateY = useSharedValue(0);
const pinchGesture = Gesture.Pinch()
.onUpdate((event) => {
scale.value = savedScale.value * event.scale;
})
.onEnd(() => {
savedScale.value = scale.value;
});
const panGesture = Gesture.Pan()
.onUpdate((event) => {
translateX.value = event.translationX;
translateY.value = event.translationY;
})
.onEnd(() => {
translateX.value = withSpring(0);
translateY.value = withSpring(0);
});
const composed = Gesture.Simultaneous(pinchGesture, panGesture);
const animatedStyle = useAnimatedStyle(() => ({
transform: [
{ scale: scale.value },
{ translateX: translateX.value },
{ translateY: translateY.value },
],
}));
return (
<GestureDetector gesture={composed}>
<Animated.Image
source={{ uri: imageUrl }}
style={[styles.image, animatedStyle]}
/>
</GestureDetector>
);
};
2. 手势优先级控制
jsx
const PriorityExample = () => {
const horizontalPan = Gesture.Pan()
.activeOffsetX([-10, 10])
.onStart(() => console.log('Horizontal pan'));
const verticalPan = Gesture.Pan()
.activeOffsetY([-10, 10])
.onStart(() => console.log('Vertical pan'));
const tap = Gesture.Tap()
.onStart(() => console.log('Tap'));
// 水平拖拽优先于垂直拖拽,点击最后处理
const gestures = Gesture.Race(
horizontalPan,
Gesture.Race(verticalPan, tap)
);
return (
<GestureDetector gesture={gestures}>
<View style={styles.priorityBox}>
<Text>Try different gestures</Text>
</View>
</GestureDetector>
);
};
总结
指令式 API 的主要优势
- 声明式配置:链式调用让配置更直观
- 更好的组合性:组合器让复杂手势关系更清晰
- 类型安全:完整的 TypeScript 支持
- 性能优化:减少组件嵌套,优化渲染性能
- 现代化:符合 React Hooks 和函数式编程趋势
迁移策略
对于新项目,强烈推荐使用指令式 API。对于现有项目,可以逐步迁移:
- 在新功能中使用指令式 API
- 逐步重构复杂的手势逻辑
- 利用组合器简化手势关系管理
指令式 API 代表了 React Native Gesture Handler 的未来发展方向,它提供了更强大、更灵活的手势处理能力,同时保持了优秀的性能表现。