react-native系列(21)API篇:手势PanResponder详解

本文深入解析了PanResponder库在React Native中的应用,包括如何创建和配置手势识别,以及如何处理各种手势事件,如触摸、滑动和缩放。文章还提供了一个单点触摸拖动效果的代码实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

PanResponder库用于处理用户的手势操作,如单点触摸滑屏、多点触摸放大缩小等手势。

PanResponder的创建代码如下:

this._panResponder = PanResponder.create({
    // 返回ture时,表示该组件愿意成为触摸事件的响应者,如触摸点击。默认返回false。
    onStartShouldSetPanResponder: () => true,  
    // 返回ture时,表示该组件愿意成为触摸(滑屏)事件的响应者,如触摸滑屏,默认返回false。
    onMoveShouldSetPanResponder: () => true, 
    // 与onStartShouldSetPanResponder相同,当此组件A里包含了子组件B也为触摸事件响应者时,若此时设为true,则父组件A优先级更高
    onStartShouldSetPanResponderCapture: () => true, 
     // 与onMoveShouldSetPanResponder相同,当此组件A里包含了子组件B也为触摸事件响应者时,若此时设为true,则父组件A优先级更高
    onMoveShouldSetPanResponderCapture: () => true,
    // 手势刚开始触摸(即刚接触屏幕时)时,若响应成功则触发该事件
    onPanResponderGrant: (evt, gestureState) => {},    
    // 手势刚开始触摸(即刚接触屏幕时)时,若响应失败则触发该事件,失败原因有可能是其它组件正在响应手势且不肯放权
    onResponderReject: (evt, gestureState) => {}, 
    // 手势滑动时触发该事件
    onPanResponderMove: (evt, gestureState) => {},    
    // 手势松开时触发该事件
    onPanResponderRelease: (evt,gestureState) => {},    
    // 当其它组件需要响应手势时,此时为ture则表示本组件愿意放权给其它组件响应;为false时表示不放权,依然由本组件来响应手势事件
    onPanResponderTerminationRequest: (evt, gestureState) => true, 
    // 当组件响应放权后(即由其它组件拿到了手势响应权)触发该事件
    onPanResponderTerminate: (evt, gestureState) => {} 
});

监听的事件中都会返回2个回调参数,evt 和 gestureState。

回调参数1: evt字段下的nativeEvent(evt.nativeEvent)对象格式如下:

属性描述

changedTouches

所有发生变化的触摸事件的数组集合,该属性数据可用于多个触摸点

identifier

触摸点的 ID

locationX

触摸点相对于父元素的横坐标

locationY

触摸点相对于父元素的纵坐标

pageX

触摸点相对于根元素的横坐标

pageY

触摸点相对于根元素的纵坐标

target

触摸点所在的元素 ID

timestamp

触摸事件的时间戳,可用于移动速度的计算

touches

当前屏幕上的所有触摸点的集合。与changedTouches区别在于:当响应拖动Move事件时,与changedTouches数据一样。在响应释放Release事件时,touches将没有数据而changedTouches有。

回调参数2:gestureState字段格式:
 

属性描述

stateID

触摸状态的 ID。在屏幕上有至少一个触摸点的情况下,这个 ID 会一直有效。

moveX

触摸点相对于根元素的横坐标,该坐标值会随着手势移动而变化

moveY

触摸点相对于根元素的纵坐标,该坐标值会随着手势移动而变化

x0

当刚发生手势响应时触摸点相对于根元素的横坐标,该坐标值不会随着手势移动而变化

y0

当刚发生手势响应时触摸点相对于根元素的纵坐标,该坐标值不会随着手势移动而变化

dx

从触摸操作开始时的累计横向路程

dy

从触摸操作开始时的累计纵向路程

vx

当前的横向移动速度

vy

当前的纵向移动速度

numberActiveTouches

当前在屏幕上的有效触摸点的数量,如当只有一根手指头触摸屏幕时,值为1

以下是一个单点触摸拖动效果的代码:

import React from 'react';
import { View, PanResponder, StyleSheet } from 'react-native';

// 触屏手势 
class PanResponderAPI extends React.Component {

    UNSAFE_componentWillMount() {
        this._previousLeft = 0;
        this._previousTop = 0;
        this._circleStyles = {
          style: {
            left: this._previousLeft,
            top: this._previousTop
          },
        };
    }

    constructor(props){
        super(props);
        this._panResponder = PanResponder.create({
            onStartShouldSetPanResponder: () => true,  // 返回ture时,表示该组件愿意成为触摸事件的响应者,如触摸点击。默认返回false。
            onMoveShouldSetPanResponder: () => true, // 返回ture时,表示该组件愿意成为触摸(滑屏)事件的响应者,如触摸滑屏,默认返回false。
            onStartShouldSetPanResponderCapture: () => true, // 与onStartShouldSetPanResponder相同,当此组件A里包含了子组件B也为触摸事件响应者时,若此时设为true,则父组件A优先级更高
            onMoveShouldSetPanResponderCapture: () => true, // 与onMoveShouldSetPanResponder相同,当此组件A里包含了子组件B也为触摸事件响应者时,若此时设为true,则父组件A优先级更高
            onPanResponderGrant: () => {},    // 手势刚开始触摸(即刚接触屏幕时)时,若响应成功则触发该事件
            onResponderReject: () => {}, // 手势刚开始触摸(即刚接触屏幕时)时,若响应失败则触发该事件,失败原因有可能是其它组件正在响应手势且不肯放权
            onPanResponderMove: (evt, gestureState) => {
                this._circleStyles.style.left = this._previousLeft + gestureState.dx;
                this._circleStyles.style.top = this._previousTop + gestureState.dy;
                this._updateNativeStyles();
            },    // 手势滑动时触发该事件
            onPanResponderRelease: (evt,gestureState) => {
                // 写法 一
                // this._previousLeft = evt.nativeEvent.pageX-evt.nativeEvent.locationX;
                // this._previousTop = evt.nativeEvent.pageY-evt.nativeEvent.locationY;
                // 写法 二
                this._previousLeft += gestureState.dx;
                this._previousTop += gestureState.dy;
            },    // 手势松开时触发该事件
            onPanResponderTerminationRequest: () => true, // 当其它组件需要响应手势时,此时为ture则表示本组件愿意放权给其它组件响应;为false时表示不放权,依然由本组件来响应手势事件
            onPanResponderTerminate: () => {} // 当组件响应放权后(即由其它组件拿到了手势响应权)触发该事件
        });
    }

    _updateNativeStyles() {
        this.circle && this.circle.setNativeProps(this._circleStyles);
    }

    render(){
        return(
            <View style={{backgroundColor:'green', flex:1, marginLeft: 100}}>
                <View
                    ref={circle  => {
                        this.circle  = circle ;
                    }} 
                    style={[styles.container]}
                    {...this._panResponder.panHandlers}
                ></View>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        width: 100,
        height: 100,
        borderWidth: 1,
        borderRadius: 100,
        backgroundColor: 'yellow'
    }
});

export default PanResponderAPI;

效果:

这里有一个细节,当黄球拖动出绿色区域后,将无法拖动。因为PanResponder的事件传递仅冒泡当父元素一级就会停止冒泡,故拖动黄球到白色区域(其父元素以外的层级)时并不会触发onPanResponderMove事件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值