react-native-reanimated-carousel
react-native-reanimated-carousel copied to clipboard
onScrollBegin triggers on single tap but onScrollEnd does not
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:
- Load the Carousel with required data
- Add
onScrollBegin={() => console.log('start')} onScrollEnd={() => console.log('end')}
- Tap on the Carousel
- 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):
- react: v17.0.2
- react-native: v0.68.2
- react-native-reanimated: v2.8.0
- react-native-reanimated-carousel: v3.0.4
- react-native-gesture-handler: v2.2.1
Smartphone (please complete the following information):
- Device: iPhone 13 Pro
- OS: 15.1
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 ^^
Thanks for your detailed reproduction. I'll check it ASAP .
Same issue here. :( Did you find any workaround @andonovn?
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);
}
};
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.
@martwetzels
This is the exact issue I'm having (even came from the same SO question). Did you end up finding a solution?
@davidwagn not with this library, I ended up implementing something myself that got the job done.
Same issue :( Any solutions?
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>
)}
/>
)
}
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);
}
}
}}
/>
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;
}
}
}}
/>