user1985427
user1985427

Reputation: 363

In React Native, is it possible to detect a swipe vs a tap in Pan Responder's onStartShouldSetPanResponder?

Right now, I return a true in my onStartShouldSetPanResponder, and as a result the PanResponder wants to handle taps AND pans. Is there any way to restrict it to just pans, as I want a TouchableHighlight to handle that? (I get that the Gesture Responder should handle both, but it seems weird that the "Pan" Responder handles taps)

Since the gesture is just starting, the dx/dy are 0 in onStartShouldSetPanResponder. Is there any way to detect if it's the start of a tap and return false, if so?

Or should I just detect whether it was a tap or pan in the OnPanResponderRelease?

Upvotes: 3

Views: 4473

Answers (2)

JPritchard9518
JPritchard9518

Reputation: 192

I was able to accomplish this through the onMoveShouldSetPanResponder method as follows:

onMoveShouldSetPanResponder: (evt, gestureState) => {
    return Math.abs(gestureState.dx) >= 1 || Math.abs(gestureState.dy) >= 1
}

If the x or y movement is greater than 1, return true. In order to then detect a tap, I had to wrap everything within my view containing the panHandlers with a touchable element. Here is a full working example:

import React, { Component } from 'react';
import { TouchableOpacity, Animated, PanResponder, Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';

export default function App() {
  return (
    <View>
      <CircleTapExample/>
    </View>
  );
}

class CircleTapExample extends Component {
    constructor(props) {
        super(props)
        this.position = new Animated.ValueXY({ x: 0, y: 0 });
        this.panResponder = PanResponder.create({
            onMoveShouldSetPanResponder: (evt, gestureState) => {
              return Math.abs(gestureState.dx) >= 1 || Math.abs(gestureState.dy) >= 1
            },
            onPanResponderMove: (evt, gestureState) => {
                console.log("I was moved")
                this.position.setValue({ x: gestureState.moveX, y: gestureState.moveY })
            },
        });
    }
    
    circleTapped() {
        // Do something here when tapped
        console.log("I was tapped")
    }
    
    render() {
        return (
            <Animated.View style={[styles.container, { ...this.position.getLayout() }]} {...this.panResponder.panHandlers}>
                <TouchableOpacity onPress={() => this.circleTapped()} style={{ flex: 1 }}>
                    <View style={styles.circle} />
                </TouchableOpacity>
            </Animated.View>
        )
    }
}

const styles = StyleSheet.create({
  container: {
      width: 75,
      height: 75,
      bottom: 5,
      left: 5,
      position: 'absolute'
  },
  circle: {
      width: 75,
      height: 75,
      borderRadius: 40,
      backgroundColor: 'red'
  }
});

<div data-snack-id="YskU-lxRe" data-snack-platform="web" data-snack-preview="true" data-snack-theme="light" style="overflow:hidden;background:#F9F9F9;border:1px solid var(--color-border);border-radius:4px;height:505px;width:100%"></div>
<script async src="https://snack.expo.dev/embed.js"></script>

Upvotes: 4

ancyrweb
ancyrweb

Reputation: 1313

The panResponder has two events :

  • onStartShouldSetPanResponder(Capture)
  • onMoveShouldSetPanResponder(Capture)

I've been able to solve this problem only by removing onStartShouldSetPanResponderCapture.

Upvotes: 1

Related Questions