这一小节我们来看一下 RN 中和手势相关的 API。
文档地址:https://reactnative.cn/docs/panresponder
我们先来看一个简单的例子:
import React from 'react'import { PanResponder, StyleSheet, View } from 'react-native'
export default function App() { const panResponder = PanResponder.create({ onStartShouldSetPanResponder: function () { console.log('shouldstart') return true }, onPanResponderMove: function () { console.log('moving') }, onPanResponderRelease: function () { console.log('release') }, })
console.log(panResponder.panHandlers)
return ( <View style={styles.container}> <View style={styles.box} {...panResponder.panHandlers}></View> </View> )}
const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', }, box: { backgroundColor: '#61dafb', width: 80, height: 80, borderRadius: 4, },})在上面的示例中,我们通过 PanResponder 这个 API 的 create 方法来创建一个手势方法的集合对象。该方法接收一个配置对象,配置对象中能够传递的参数如下:
可以看到,配置对象对应的每一个配置值都是一个回调函数,每个回调函数都接收两个参数,一个是原生事件对象,另一个是 gestureState 对象。
nativeEvent 原生事件对象有如下字段:
- changedTouches - 在上一次事件之后,所有发生变化的触摸事件的数组集合(即上一次事件后,所有移动过的触摸点)
- identifier - 触摸点的 ID
- locationX - 触摸点相对于父元素的横坐标
- locationY - 触摸点相对于父元素的纵坐标
- pageX - 触摸点相对于根元素的横坐标
- pageY - 触摸点相对于根元素的纵坐标
- target - 触摸点所在的元素 ID
- timestamp - 触摸事件的时间戳,可用于移动速度的计算
- touches - 当前屏幕上的所有触摸点的集合
一个 gestureState 对象有如下的字段:
- stateID - 触摸状态的 ID。在屏幕上有至少一个触摸点的情况下,这个 ID 会一直有效。
- moveX - 最近一次移动时的屏幕横坐标
- moveY - 最近一次移动时的屏幕纵坐标
- x0 - 当响应器产生时的屏幕坐标
- y0 - 当响应器产生时的屏幕坐标
- dx - 从触摸操作开始时的累计横向路程
- dy - 从触摸操作开始时的累计纵向路程
- vx - 当前的横向移动速度
- vy - 当前的纵向移动速度
- numberActiveTouches - 当前在屏幕上的有效触摸点的数量
例如我们通过 gestureState 对象来判断用户手指的移动方向:
const panResponder = PanResponder.create({ onStartShouldSetPanResponder: function () { console.log('shouldstart') return true }, onPanResponderMove: function (e, gs) { console.log(`正在移动: X轴: ${gs.dx}, Y轴: ${gs.dy}`) }, onPanResponderRelease: function (e, gs) { console.log(`结束移动: X轴移动了: ${gs.dx}, Y轴移动了: ${gs.dy}`) if (gs.dx > 50) { console.log('由左向右') } else if (gs.dx < -50) { console.log('由右向左') } else if (gs.dy > 50) { console.log('由上向下') } else if (gs.dy < -50) { console.log('由下向上') } },})最后,我们把上一节课介绍的 Animated 结合起来,书写一个拖动小方块的示例:
import { useState } from 'react'import { Animated, PanResponder, StyleSheet, View } from 'react-native'
export default function App() { const [transXY] = useState(new Animated.ValueXY())
const panResponder = PanResponder.create({ onStartShouldSetPanResponder: function () { console.log('shouldstart') return true }, onPanResponderMove: Animated.event( [ null, { dx: transXY.x, dy: transXY.y, }, ], { useNativeDriver: false } ),
onPanResponderRelease: function () { Animated.spring(transXY, { toValue: { x: 0, y: 0 }, useNativeDriver: false, }).start() }, })
return ( <View style={styles.container}> <Animated.View style={[ styles.box, { transform: [{ translateX: transXY.x }, { translateY: transXY.y }], }, ]} {...panResponder.panHandlers} /> </View> )}
const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', }, box: { backgroundColor: '#61dafb', width: 80, height: 80, borderRadius: 4, },})