react-native-reanimated-carousel: onProgressChanged and onSnapToItem takes too long to give back an index

Describe the bug onProgressChanged and onSnapToItem takes too long to give back an index If I want to set a scrollAnimationDuration of 1000 for example, and use onSnapToItem to set the index of the dot in a pagination component, it takes 1sec to update.

Expected behavior Gives the next index immediately on swipe. Not wait until it has snapped and settled.

Versions (please complete the following information):

  • react: v17.0.2
  • react-native: v0.68.2
  • react-native-reanimated-carousel: v3.0.6

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Reactions: 2
  • Comments: 16 (5 by maintainers)

Most upvoted comments

onProgressChange shows that the index does change when progress reaches 50% (call getCurrentIndex inside onProgressChange). Setting state in onProgressChange seems to work well on device - not so good on sim.

Edit: I originally thought this wasn’t working well until I tried on device rather than sim.

              onProgressChange={(
                _offsetProgress: number,
                absoluteProgress: number,
              ) => {
                if (
                  carouselRef.current &&
                  (absoluteProgress > 0.5 ||
                    carouselRef.current?.getCurrentIndex() === 0)
                ) {
                  setActiveSlide(carouselRef.current.getCurrentIndex());
                }
              }}

Slightly cleaner version of the above fix

onProgressChange={(_offsetProgress: number, absoluteProgress: number) => {
  const currentIndex = carouselRef.current?.getCurrentIndex() || 0;
  
  if (absoluteProgress > 0.5 || currentIndex === 0) {
    setTutorialImageIndex(currentIndex);
  }
}}

This question is about timing, and I’ll update the new index after the animation is done rather than begin, but onProgressChange always be synced. Why It took a long time? Maybe I can add new Props to get the next index information before the animation. Be like onBeforeSnapToItem. But there’s no way to calculate exactly for onBeforeSnapToItem callback until the animation stops completely. How do you calculate the final index? e.g. If you scroll for half of the screen width, the index will doesn’t change. It will scroll back.

Slightly cleaner version of the above fix

onProgressChange={(_offsetProgress: number, absoluteProgress: number) => {
  const currentIndex = carouselRef.current?.getCurrentIndex() || 0;
  
  if (absoluteProgress > 0.5 || currentIndex === 0) {
    setTutorialImageIndex(currentIndex);
  }
}}

The getCurrentIndex function strangely returns 0, 1, 2, and 3 when the length of the data is 2. I expect it to return either 0 or 1. This issue does not occur when the length of the data is more than 3. Is this an issue that only I am encountering?

It seems to work well when I use the following code:

onProgressChange={(_, absoluteProgress) => {
  const currentIndex = Math.round(absoluteProgress) % data.length;
  setTutorialImageIndex(currentIndex);
}}

Faced the same problem. If you scroll very quickly, onSnapToItem is not called at all. Temporary solution (updated):

const carouselRef = useRef<ICarouselInstance>();
const isNewSwap = useRef(false);

onScrollBegin={() => {
  isNewSwap.current = true;
}}
onProgressChange={(_, absoluteProgress) => {
  const progress = absoluteProgress - carouselRef.current.getCurrentIndex();

  if (carouselRef.current && Math.abs(progress) >= 0.45 && isNewSwap.current) {
    isNewSwap.current = false;

    setTimeout(() => {
      setWallpaperIndex(carouselRef.current.getCurrentIndex());
    }, 50);
  }
}}

setWallpaperIndex - custom function which change state

But there’s no way to calculate exactly for onBeforeSnapToItem callback until the animation stops completely. How do you calculate the final index? e.g. If you scroll for half of the screen width, the index will doesn’t change. It will scroll back.

In this case you would call onBeforeSnapToItem again with the previous index

Re. #267. I’ll make a simple pagination component for basic usage. OHHHHH, Sry! I just understood what you meant. You want a simple way to control the pagination state rather than delicate animation. Let me think it over.

Maybe this new prop isn’t necessary, and you’ll solve the problem when you figure out the correct usage of reanimated. 🚀

@dohooo ahh I see the thinking behind the decision. I’m still confused how the old carousel library did this - the snap-carousel. The index seems to change a lot smoother in that one. Here it almost feels like it takes about 200-300ms before actually changing the index even after snapping.

oh, I know what you mean. The timing function causes this.

withTiming withSpring

image

When you think the animation is done and the index should be changing, but actually, the animation isn’t done because the timing function is causing that. You should read the documentation of reanimated. 🥸