'Detect Swipe direction using React Native Gesture Handler and Reanimated

We have already developed a swiper using React Native PanResponder, (before somebody comment) - we cannot use any other swiper library here. In the swiper we developed, we are facing an issue when a user ends the swipe(meaning releases the Pan) there is a lag when starting a spring animation.

So to fix that, we are trying to move from React Native Animated API to Reanimated Libary which could solve the problem of lag the user is facing.

But while developing with React Native Gesture Handler (PanGestureHandler) and Reanimated Library, we're unable to detect swipe direction in the gesture handler.

I'm adding the part of code which we used to detect swipe direction in PanResponder

onPanMoving = (evt, {dx}) => {
   this.draggedEnd = dx < -this.swipeThreshold; //You are moving towards right screen
   this.draggedStart = dx > this.swipeThreshold; //You are moving towards left screen
}

As you see, detecting the direction while the pan is moving was so easy in PanResponder.

Here comes the problem with Gesture Handler,

I'm unable to detect swipe while the gesture state is active

I have already searched for issues in Gesture Handler and I found this.

There were two workaround suggested in the issue

  1. First one is by Sonaye which has two handler and detects the swipe direction in onHandlerStateChange for both the handlers which was not helpful while using reanimated because it only sets swipe direction when handlers state changes.
  2. Second one is by Satya which actually detects the swipe when the State is ACTIVE but he uses translationX property, the translationX property can also negative for us and can move in either direction similar to swiper.

Both the workarounds doesn't solve our problem.

So is there any other way to find direction using PanGestureHandler and Reanimated. I tried using PanResponder with Reanimated with dx value but ended up with error message i.e nativeEvent properties are only support as dx is from gestureState param from PanResponder.

Note: We cannot use FlingGestureHandler and Swipeables because we need to find direction in Fling still onHandlerStateChange and Swipeables doesn't use Reanimated.



Solution 1:[1]

You can detect direction when swipe horizontal via property velocityX

Example :

const handleGesture = (evt) => {
    const { nativeEvent } = evt;

    if (nativeEvent.velocityX > 0) {
      console.log('Swipe right');
    } else {
      console.log('Swipe left');
    }
  };

return (
   <PanGestureHandler onGestureEvent={handleGesture}>
       // child component
   </PanGestureHandler>
);


Solution 2:[2]

For those who want to use double FlingGestureHandler to have vertical or horizontal fling gesture here is the solution:

First you have to initialize the stateHandlers for the direction you want. In my case, i was trying to implement a vertical flingGestureHandler.

  const onDownFlingHandlerStateChange = ({
    nativeEvent,
  }: FlingGestureHandlerStateChangeEvent) => {
    if (nativeEvent.oldState === State.ACTIVE) {
      progressHeight.value = withTiming(COLLAPSED_HEIGHT, {
        duration: ANIMATION_DURATION,
      });
    }
  };

  const onUpFlingHandlerStateChange = ({
    nativeEvent,
  }: FlingGestureHandlerStateChangeEvent) => {
    if (nativeEvent.oldState === State.ACTIVE) {
      progressHeight.value = withTiming(
        DEVICE_HEIGHT - TOP_PADDING_FOR_EXPANDED,
        {
          duration: ANIMATION_DURATION,
        },
      );
    }
  };

and then you have to bind those handlers to 2 FlingGestureHandler component of RNGH

<FlingGestureHandler
  onHandlerStateChange={onUpFlingHandlerStateChange}
  onEnded={() => {
     onPressChecklistIcon();
  }}
  direction={Directions.UP}>
  <FlingGestureHandler
    direction={Directions.DOWN}
    onEnded={() => {
      onPressChecklistIcon();
    }}
    onHandlerStateChange={onDownFlingHandlerStateChange}>
    <Animated.View style={[styles.container, reanimatedStyle]}>
      <View style={styles.closerContainer}>
        <View style={styles.closer} />
      </View>
      {renderChecklistContent()}
    </Animated.View>
  </FlingGestureHandler>
</FlingGestureHandler>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Thunder Clap
Solution 2 AlkanV