dohooo / react-native-reanimated-carousel

🎠 React Native swiper/carousel component, fully implemented using reanimated v2, support to iOS/Android/Web. (Swiper/Carousel)
https://react-native-reanimated-carousel.vercel.app
MIT License
2.72k stars 314 forks source link

onScrollBegin is firing when scrolling down inside a ScrollView and then onScrollEnd does not fire. #611

Closed YoucefBen47 closed 4 months ago

YoucefBen47 commented 4 months ago

Describe the bug We have a default Horizontal Carousel inside of a ScrollView. We use onScrollEnd and onScrollBegin to animate a text on swipe. When scrolling down away from the Carousel the onScrollBegin fires but onScrollEnd doesn't. To Reproduce Steps to reproduce the behavior:

  1. Put a default Carousel inside a scroll view.
  2. Scroll down.

Expected behavior onScrollBegin should not fire because the items are not being swiped horizontally.

Versions (please complete the following information):

Smartphone (please complete the following information):

dosubot[bot] commented 4 months ago

To address the issue where onScrollBegin fires on vertical scrolls within a ScrollView, you'll need to differentiate between horizontal and vertical gestures. This can be achieved by modifying the gesture handling logic to only trigger onScrollBegin for horizontal movements. Here's a conceptual approach:

const onGestureUpdate = useCallback((e: PanGestureHandlerEventPayload) => {
  "worklet";
  const { translationX, translationY } = e;
  let isVerticalScroll = Math.abs(translationY) > Math.abs(translationX);

  if (!isVerticalScroll) {
    // Trigger onScrollBegin for horizontal scrolls
  }
}, []);

For the onScrollEnd not firing, a workaround involves using the onProgressChange event to manage a scrolling state, which then determines when scrolling starts and ends. This approach replaces the direct use of onScrollBegin and onScrollEnd:

const [scrolling, setScrolling] = useState(false);

useEffect(() => {
  if (scrolling) {
    // Handle onScrollBegin actions
  } else {
    // Handle onScrollEnd actions
  }
}, [scrolling]);

const onProgressChange = (_: number, absoluteProgress: number) => {
  setScrolling(absoluteProgress % 1 !== 0);
};

This solution should help manage the scroll events more accurately for your carousel within a ScrollView, ensuring that onScrollBegin is only triggered for relevant horizontal gestures and providing a mechanism to detect the end of scrolling.

#### Details **Sources** - [Horizontal Carousel scrolling conflicts with vertical ScrollView scrolling](https://github.com/dohooo/react-native-reanimated-carousel/discussions/588) - [this library not providing native scroll events onScrollBeing or onScrollEnd](https://github.com/dohooo/react-native-reanimated-carousel/issues/602) - [onScrollBegin triggers on single tap but onScrollEnd does not](https://github.com/dohooo/react-native-reanimated-carousel/issues/244) - [Carousel in ScrollView layout](https://github.com/dohooo/react-native-reanimated-carousel/issues/383) - [Some kind of onScroll prop needed](https://github.com/dohooo/react-native-reanimated-carousel/issues/227) - [Hi](https://github.com/dohooo/react-native-reanimated-carousel/issues/218) - [src/components/Carousel.tsx](https://github.com/dohooo/react-native-reanimated-carousel/blob/main/src/components/Carousel.tsx) - [src/components/ScrollViewGesture.tsx](https://github.com/dohooo/react-native-reanimated-carousel/blob/main/src/components/ScrollViewGesture.tsx)
**About Dosu** This response is meant to be useful and save you time. It is **not meant to be a precise solution**, but rather a starting point for your own research. Help me be more useful! Please leave a 👍 if this is helpful and 👎 if it is irrelevant.

To continue the conversation, mention @dosu.

YoucefBen47 commented 4 months ago

I ended up using this: https://github.com/dohooo/react-native-reanimated-carousel/issues/244#issuecomment-1253386041

 onProgressChange={(_, absoluteProgress) => {
          if (absoluteProgress % 1 !== 0) {
            fadeOut();
          } else {
            fadeIn();
          }
        }}
talhabinkhalil commented 6 days ago

To address the issue where onScrollBegin fires on vertical scrolls within a ScrollView, you'll need to differentiate between horizontal and vertical gestures. This can be achieved by modifying the gesture handling logic to only trigger onScrollBegin for horizontal movements. Here's a conceptual approach:

const onGestureUpdate = useCallback((e: PanGestureHandlerEventPayload) => {
  "worklet";
  const { translationX, translationY } = e;
  let isVerticalScroll = Math.abs(translationY) > Math.abs(translationX);

  if (!isVerticalScroll) {
    // Trigger onScrollBegin for horizontal scrolls
  }
}, []);

For the onScrollEnd not firing, a workaround involves using the onProgressChange event to manage a scrolling state, which then determines when scrolling starts and ends. This approach replaces the direct use of onScrollBegin and onScrollEnd:

const [scrolling, setScrolling] = useState(false);

useEffect(() => {
  if (scrolling) {
    // Handle onScrollBegin actions
  } else {
    // Handle onScrollEnd actions
  }
}, [scrolling]);

const onProgressChange = (_: number, absoluteProgress: number) => {
  setScrolling(absoluteProgress % 1 !== 0);
};

This solution should help manage the scroll events more accurately for your carousel within a ScrollView, ensuring that onScrollBegin is only triggered for relevant horizontal gestures and providing a mechanism to detect the end of scrolling.

Details

To continue the conversation, mention @dosu.

This is not working, when loop is false and we are on the last item of carousel. When we scroll to left absoluteProgress is 1 so setScroll will not set to True