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.63k stars 300 forks source link

onScrollBegin triggers on single tap but onScrollEnd does not #244

Open andonovn opened 1 year ago

andonovn commented 1 year ago

Hey there, thanks for the nice library ^^

I think the behaviour for onScrollBegin and onScrollEnd should be the same once a user do a single tap on the carousel. Currently, the first one gets executed while the latter is not.

To Reproduce Steps to reproduce the behavior:

  1. Load the Carousel with required data
  2. Add onScrollBegin={() => console.log('start')} onScrollEnd={() => console.log('end')}
  3. Tap on the Carousel
  4. See 'start' but not 'end'

Expected behavior Either both callbacks should be executed, OR neither of the callbacks should not be executed

Versions (please complete the following information):

Smartphone (please complete the following information):

Haven't tested on other devices, my blind guess is it will work the same across all the devices but I may be terribly wrong :)

Not sure if this is considered a bug but in our case it is because we use the both callbacks to toggle absolutely positioned elements on the screen, outside the Carousel. Any suggestions for work-arounds are welcomed ^^

dohooo commented 1 year ago

Thanks for your detailed reproduction. I'll check it ASAP .

sylvaindubus commented 1 year ago

Same issue here. :( Did you find any workaround @andonovn?

sylvaindubus commented 1 year ago

I've managed to reproduce the same behavior using onProgressChange:

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

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

const onProgressChange = (_: number, absoluteProgress: number) => {
  if (absoluteProgress % 1 !== 0) {
    setScrolling(true);
  } else {
    setScrolling(false);
  }
}; 
martwetzels commented 1 year ago

I'm experiencing this issue as well, for context see SO, where the onScrollBegin and onScrollEnd are used to prevent Pressable child components from being triggered. The onScrollBegin gets triggered at a single tap (expected gesture for Pressing) instead of at actually scrolling (swiping gesture) the carousel.

davidwagn commented 1 year ago

@martwetzels

This is the exact issue I'm having (even came from the same SO question). Did you end up finding a solution?

martwetzels commented 1 year ago

@davidwagn not with this library, I ended up implementing something myself that got the job done.

Vepsur commented 1 year ago

Same issue :( Any solutions?

AlexanderCollins commented 1 year ago

I'm not sure if this is entirely related, maybe I've missed the point of this thread. My issue was with being able to detect when a user taps a child of the carousel vs swiping through the carousel itself... I wanted to at least put this here to help anyone that needs a solution in the mean time even if it's not ideal...

I ended up having to use state to track when a child item is pressed and checked the change in x movement on the onPressOut event to be able to detect pressing children vs swiping through the carousel...

const MyComponent = () => {
    const [pressState, setPressState] = useState({});
    return (
        <Carousel
            ...
            renderItem={({ index }) => (
                <Pressable
                  onPressIn={(event) => {   
                    setPressState({
                      ...pressState,
                      [index]: event.nativeEvent.pageX,
                    });
                  }}
                  onPressOut={(event) => {
                    const delta = event.nativeEvent.pageX - pressState[index] || 0;
                    if (Math.abs(delta) < 5) {
                      // handle item pressed here
                    }
                  }}
                  key={index}
                >
                  {/* Child Item */}
                </Pressable>
              )}
        />
    )
}
ajp8164 commented 1 year ago

I'm experiencing the same/similar issue. I'm not using onScrollBegin or onScrollEnd but on app launch the first attempt to swipe the carousel left/right results in the child (card) receiving a touch event. This only happens on first swipe try after launch. It never happens again until next app launch.

Here's my carousel. The presence of onProgressChange does not make a difference.

      <Carousel
        ref={carouselRef}
        data={cards}
        renderItem={renderCard}
        loop={false}
        width={viewport.width}
        style={{ overflow: 'visible' }}
        onProgressChange={() => {
          if (carouselRef.current) {
            const currentSlide = carouselRef.current.getCurrentIndex();
            if (currentSlide !== activeSlideRef.current) {
              activeSlideRef.current = currentSlide;
              onScrollIndexChanged(currentSlide);
            }
          }
        }}
      />
MatLish00010 commented 11 months ago

const animationState = useRef({ shouldBeFinished: false, started: false, });

  <Carousel
    ....
    onScrollEnd={() => {
    if (animationFinished && animationStarted) {
      animationState.current.shouldBeFinished = true;
    }
  }}
  onProgressChange={() => {
    if (animationFinished && animationStarted) {
      if (animationState.current.shouldBeFinished) {
        animationFinished();
        animationState.current.shouldBeFinished = false;
        animationState.current.started = false;
      } else if (!animationState.current.started) {
        animationStarted();
        animationState.current.started = true;
      }
    }
  }}
  />