Skip to content

手势API

这一小节我们来看一下 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 这个 APIcreate 方法来创建一个手势方法的集合对象。该方法接收一个配置对象,配置对象中能够传递的参数如下:

image-20220623150525365

可以看到,配置对象对应的每一个配置值都是一个回调函数,每个回调函数都接收两个参数,一个是原生事件对象,另一个是 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,
},
})