React-native中的手势系统
移动设备上的手势识别要比在web上复杂得多。用户的一次触摸操作的真实意图是什么,App要经过好几个阶段才能判断。比如App需要判断用户的触摸到底是在滚动页面,还是滑动一个widget,或者只是一个单纯的点击。甚至随着持续时间的不同,这些操作还会转化。此外,还有多点同时触控的情况。
触摸响应系统可以使组件在不关心父组件或子组件的前提下自行处理触摸交互。
手势组件
react-native中预置的处理手势的组件
react-native中是没有click等事件的,需要借助封装的好的组件配合(onpress
或`onLongPress)完成手势处理
组件:
TouchableHighlight
触摸屏幕会有高亮显示TouchableNativeFeedback
触摸屏幕会有水波纹效果(仅限Android)TouchableOpacity
触摸屏幕改变当前元素的透明度TouchableWithoutFeedback
触摸屏幕无任何效果
事件:
onPress
点击组件时触发onLongPress
长按组件时触发
手势系统
单单依靠单击和长按是无法满足移动App多种手势的需求的,此时需要利用收拾系统。
引入
1
import { PanResponder } from "react-native";
配置手势
- 处理函数:
onStartShouldSetPanResponder
用户开始触摸屏幕的时候,是否愿意成为响应者;默认返回false,无法响应,当返回true的时候则可以进行之后的事件传递。默认为falseonMoveShouldSetPanResponder
在每一个触摸点开始移动的时候,再询问一次是否响应触摸交互;认为falseonPanResponderGrant
开始手势操作,也可以说按下去。给用户一些视觉反馈,让他们知道发生了什么事情!(如:可以修改颜色)onPanResponderMove
最近一次的移动距离.如:(获取x轴y轴方向的移动距离gestureState.dx,gestureState.dy
)onPanResponderRelease
用户放开了所有的触摸点,且此时视图已经成为了响应者。onPanResponderTerminate
另一个组件已经成为了新的响应者,所以当前手势将被取消。
- 参数
- evt
- nativeEvent
changedTouches
- 在上一次事件之后,所有发生变化的触摸事件的数组集合(即上一次事件后,所有移动过的触摸点)identifier
- 触摸点的IDlocationX
- 触摸点相对于父元素的横坐标locationY
- 触摸点相对于父元素的纵坐标pageX
- 触摸点相对于根元素的横坐标pageY
- 触摸点相对于根元素的纵坐标target
- 触摸点所在的元素IDtimestamp
- 触摸事件的时间戳,可用于移动速度的计算touches
- 当前屏幕上的所有触摸点的集合
- gestureState
dx/dy
- 手势进行到现在的横向/纵向相对位移vx/vy
- 此刻的横向/纵向速度numberActiveTouches
- responder上的触摸的个数
- nativeEvent
- evt
- 处理函数:
1 | // 一般来说 手势的创建放在 componentWillMount 生命周期中 |
使用
1
2
3render() {
return <View {...this.panResponder.panHandlers}} ></View>
}
使用手势系统实现图片拖拽
1、实现长按/释放改变图片样式
声明一个状态控制变量
1
2
3
4this.state = {
//是否开始拖动
isMove: false
};设置手势系统
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35componentWillMount(evt, gestureState) {
let self = this;
//手势创建
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => {
//当长按1秒之后使得isMove为真,开启拖拽状态
window.homeTimer = setTimeout(()=>{
console.warn('定时开启');
self.setState((state)=>{
return{
isMove: true
}
});
},1000);
return true;
},
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => {},
onPanResponderMove: (evt, gestureState) => {
if(self.state.isMove){
console.warn('正在移动');
}
},
onPanResponderRelease: (evt, gestureState) => {
//释放时,立刻清除定时器,使isMove不为真
clearTimeout(window.homeTimer);
//开启拖拽状态后,释放时关闭拖拽状态
self.setState((state)=>{
return{
isMove: false
}
});
}
});
}
利用
this.state.isMove
来控制图片状态1
2
3
4
5
6
7
8
9render() {
return (
<View style={styles.root}>
<View {...this._panResponder.panHandlers} ref='refImage' style={this.state.isMove ? styles.moveView : styles.noMoveView}>
<Image style={{width: '100%',height: '100%'}} source={require('../../assets/images/test.png')}></Image>
</View>
</View>
);
}
1 | const styles = StyleSheet.create({ |
效果:
2、实现图片移动跟随
在手指触碰到屏幕时,记录当前触碰位置(在
onPanResponderGrant
中记录)1
2
3
4
5
6
7
8onPanResponderGrant: (evt, gestureState) => {
// pageY是相对于根节点的位置,locationY是相对于元素自己
const { pageX, pageY, locationY, locationX } = evt.nativeEvent;
// 保存当前正确点击元素的位置,为了后面移动元素
this.preY = pageY - locationY;
// 保存当前正确点击元素的位置,为了后面移动元素
this.preX = pageX - locationX;
},移动时实时改变当前元素绝对定位的
left
和top
属性值(在onPanResponderMove
中改变)1
2
3
4
5
6
7
8
9
10
11
12
13onPanResponderMove: (evt, gestureState) => {
//若处于移动状态下
if (self.state.isMove) {
let top = this.preY + gestureState.dy;
let left = this.preX + gestureState.dx;
//获取当前操作对象元素
let item = this.refs.refImage;
//设置样式
item.setNativeProps({
style: { top: top, left: left },
});
}
},
3、实时监控图片位置,当满足位置条件并且手指离开屏幕时触发方法
(onPanResponderRelease
)
1 | onPanResponderRelease: (evt, gestureState) => { |
结语
React-Native中的手势系统
优点:功能强大,可以借助此工具完成多指手指,滑动退出等功能
缺点:无法确定是哪一个元素触发事件,当点击某元素时,是无法直接获取该元素,需要通过手动计算得到
我的疑问
在手势系统中的通过evt.nativeEvent.target
拿到的id值有何作用?
最后更新: 2019年09月03日 10:44
原始链接: https://HowlCN.github.io/2019/02/07/React-native中的手势系统/