Skip to content
Advertisement

React Native ScrollView inside an Animated.View to make Bumble-like swipe system

I’m currently working on a Bumble-like swipe system so I can swipe horizontally (thanks to an Animated.View and a PanResponder so I can move my view wherever I want), and vertically (because my view is longer than the height of my screen).

After a long day of search, I finally found a solution which permits to know if the user is scrolling horizontally or vertically in the PanResponder, then choose if i block the move or not

The problem is that my canMove() function’s console.log is printing null every time so only my vertical scroll is working currently. Otherwise, when i print my scrollType value in the onPanResponderMove, it changes well so I don’t understand why my canMove() function gets null

here’s my file so you can understand:

  const story = useSelector((state) => state.entities.stories[storyId]);
  const pan = useRef(new Animated.ValueXY(null, { useNativeDriver: true })).current;
  const scrollType = useRef(null);

  const checkSwipeDirection = (gestureState) => {
    if (
      (Math.abs(gestureState.dx) > Math.abs(gestureState.dy * 3))
      && (Math.abs(gestureState.vx) > Math.abs(gestureState.vy * 3))
    ) {
      scrollType.current = 'horizontal';
    } else {
      scrollType.current = 'vertical';
    }
  };
  const canMove = () => {
    console.log('scrollType.current: ', scrollType.current);
    if (scrollType.current === 'horizontal') {
      return true;
    }
    return false;
  };

  const panResponder = useRef(
    PanResponder.create({
      onMoveShouldSetPanResponder: canMove,
      onPanResponderGrant: () => {
        pan.setValue({ x: 0, y: 0 });
      },
      onPanResponderMove: (evt, gestureState) => {
        if (!scrollType.current) {
          checkSwipeDirection(gestureState);
        }
        return Animated.event(
          [null, { dx: pan.x, dy: pan.y }],
          { useNativeDriver: false },
        );
      },
      onPanResponderRelease: () => {
        Animated.spring(pan, {
          toValue: 0,
          useNativeDriver: false,
        }).start();
        scrollType.current = null;
      },
    }),
  ).current;

  return (
    <Animated.ScrollView
      {...panResponder.panHandlers}
      style={{
        transform: [{ translateX: pan.x }, { translateY: pan.y },
          {
            rotate: pan.x.interpolate({
              inputRange: [-200, 0, 200], outputRange: ['-20deg', '0deg', '20deg'],
            }),
          }],
      }}
    >
      <TouchableOpacity activeOpacity={1} style={styles.card}>
        <DiscoverCardHeader userId={story.recipient} />
        <DiscoverStory
          storyId={storyId}
          navigation={navigation}
          recipientId={story.recipient}
          authorId={story.author}
        />
      </TouchableOpacity>
    </Animated.ScrollView>
  );
};

if you need more informations i’m there to give you. hope we’ll find a solution ! thanks

Advertisement

Answer

Try this, newbies <3

const pan = useRef(new Animated.ValueXY(null, {useNativeDriver: true})).current;
var [direction, setDirection] = useState(0)

const panResponder = PanResponder.create({
    onStartShouldSetPanResponder: (evt, gestureState) => true,
    onPanResponderGrant: (evt, gestureState) => {
        setDirection(0)
        pan.setValue({x: 0, y: 0});
    },
    onPanResponderMove: (evt, gestureState) => {
        if ((direction === 0 || direction === 1) &&
            (gestureState.dy > 20 || gestureState.dy < -20)) {
            setDirection(1)
            pan.setValue({x: 0, y: 0});
        } else if ((direction === 0 || direction === 2) &&
            (gestureState.dx > 20 || gestureState.dx < -20)) {
            setDirection(2)
            pan.setValue({x: gestureState.dx, y: 0});
        }
    },
    onPanResponderRelease: () => {
        setDirection(0)
        pan.setValue({x: 0, y: 0});
    },
})


const translate = {
    transform: [
        ...[pan.getTranslateTransform()[0]]
    ]
}


return (
    <Animated.ScrollView scrollEnabled={direction !== 2} >
        <Animated.View
            {...panResponder.panHandlers}
            style={[translate]}
        >
            <View style={{
                backgroundColor: "red",
                marginTop: 100
            }}>
                <View>
                    <Text> Coucou</Text>
                    <Text style={{
                        marginTop: 1000
                    }}> Coucou juyin le chien</Text>
                </View>
            </View>
        </Animated.View>
    </Animated.ScrollView>
);
Advertisement