处理手势 - ReactNative
TODO
;动画
PanResponder
类可以将多点触摸操作协调成一个手势。它使得一个单点触摸可以接受更多的触摸操作,也可以用于识别简单的多点触摸手势。
默认情况下PanResponder
会通过InteractionManager
来阻止长时间运行的JS
事件打断当前的手势活动。
它提供了一个对触摸响应系统
响应器的可预测的包装。对于每一个处理函数,它在原生事件之外提供了一个新的gestureState
对象:
onPanResponderMove: (event, gestureState) => {}
event
,具有如下的属性:
属性 | 说明 |
---|---|
changedTouches | 在上一次事件之后,所有发生变化的触摸事件的数组集合(即上一次事件后,所有移动过的触摸点) |
identifier | 触摸点的 ID |
locationX | 触摸点相对于父元素的横坐标 |
locationY | 触摸点相对于父元素的纵坐标 |
pageX | 触摸点相对于根元素的横坐标 |
pageY | 触摸点相对于根元素的纵坐标 |
target | 触摸点所在的元素 ID |
timestamp | 触摸事件的时间戳,可用于移动速度的计算 |
touches | 当前屏幕上的所有触摸点的集合 |
一个gestureState
对象有如下的字段:
属性 | 说明 |
---|---|
stateID | 触摸状态的 ID。在屏幕上有至少一个触摸点的情况下,这个 ID 会一直有效。 |
moveX | 最近一次移动时的屏幕横坐标 |
moveY | 最近一次移动时的屏幕纵坐标 |
x0 | 当响应器产生时的屏幕坐标 |
y0 | 当响应器产生时的屏幕坐标 |
dx | 从触摸操作开始时的累计横向路程 |
dy | 从触摸操作开始时的累计纵向路程 |
vx | 当前的横向移动速度 |
vy | 当前的纵向移动速度 |
numberActiveTouches | 当前在屏幕上的有效触摸点的数量 |
componentWillMount: function() {
this._panResponder = PanResponder.create({
// 要求成为响应者:
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => {
// 开始手势操作。给用户一些视觉反馈,让他们知道发生了什么事情!
// gestureState.{x,y} 现在会被设置为0
},
onPanResponderMove: (evt, gestureState) => {
// 最近一次的移动距离为gestureState.move{X,Y}
// 从成为响应者开始时的累计手势移动距离为gestureState.d{x,y}
},
onPanResponderTerminationRequest: (evt, gestureState) => true,
onPanResponderRelease: (evt, gestureState) => {
// 用户放开了所有的触摸点,且此时视图已经成为了响应者。
// 一般来说这意味着一个手势操作已经成功完成。
},
onPanResponderTerminate: (evt, gestureState) => {
// 另一个组件已经成为了新的响应者,所以当前手势将被取消。
},
onShouldBlockNativeResponder: (evt, gestureState) => {
// 返回一个布尔值,决定当前组件是否应该阻止原生组件成为JS响应者
// 默认返回true。目前暂时只支持android。
return true;
},
});
},
render: function() {
return (
<View {...this._panResponder.panHandlers} />
);
},
代码示例
import React, { Component } from "react";
import { Animated, View, StyleSheet, PanResponder, Text } from "react-native";
class App extends Component {
pan = new Animated.ValueXY();
panResponder = PanResponder.create({
onMoveShouldSetPanResponder: () => true,
onPanResponderGrant: () => {
console.log(this.pan.x, this.pan.y);
this.pan.setOffset({
x: this.pan.x._value,
y: this.pan.y._value,
});
},
onPanResponderMove: Animated.event([
null,
{ dx: this.pan.x, dy: this.pan.y },
]),
onPanResponderRelease: () => {
this.pan.flattenOffset();
},
});
render() {
return (
<View style={styles.container}>
<Text style={styles.titleText}>Drag this box!</Text>
<Animated.View
style={{
transform: [
{ translateX: this.pan.x },
{ translateY: this.pan.y },
],
}}
{...this.panResponder.panHandlers}
>
<View style={styles.box} />
</Animated.View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
},
titleText: {
fontSize: 14,
lineHeight: 24,
fontWeight: "bold",
},
box: {
height: 150,
width: 150,
backgroundColor: "blue",
borderRadius: 5,
},
});
export default App;
上面的代码执行完毕之后,会得到诸如下面的执行动态图