meliorence / react-native-snap-carousel

Swiper/carousel component for React Native featuring previews, multiple layouts, parallax images, performant handling of huge numbers of items, and more. Compatible with Android & iOS.
BSD 3-Clause "New" or "Revised" License
10.32k stars 2.28k forks source link

Infinite loop is not working using loop={true} enableSnap ={true} in android #608

Open srichallamalla935 opened 4 years ago

srichallamalla935 commented 4 years ago

Infinite loop is not working using loop={true} , enableSnap ={true} list is not scrolling infinte mode in android

Actual Behavior

i m displaying the list in card and the card is not scrolling in the infinite mode it get strucked in middle in android

Expected Behavior

Card should be scroll in the infinite mode while scrolling

Code

 <Carousel
          ref={c => {
            this._carousel = c;
          }}
          carouselRef={this.props._slider1Ref}
          data={this.props.data}
          renderItem={this._renderItem}
          sliderWidth={this.props.sliderWidth}
          itemWidth={this.props.itemWidth}
          loop={true}
          loopClonesPerSide={this.props.loopClonesPerSide}
          firstItem={this.props.firstItem}
          onSnapToItem={index => this.onSnap(index)}
          layout={this.props.layout}
          layoutCardOffset={this.props.layoutCardOffset}
          showSpinner={this.props.showSpinner}
          activeSlideAlignment="start"
          useScrollView={false}
          activeSlideOffset={this.props.activeSlideOffset}
          lockScrollWhileSnapping={true}
          enableSnap={true}
          enableMomentum={false}
          activeSlideOffset={this.props.activeSlideOffset}
          decelerationRate="fast"
         // removeClippedSubviews={true}
          removeClippedSubviews={false}
        />

Environment

"react-native": "0.61.1", "react-native-snap-carousel": "^3.8.1"

alexaung commented 4 years ago

Same thing happening for me. It is swipe first and last item.

srichallamalla935 commented 4 years ago

Did u got any solution for infinte loop in android ?? it important for me to work it on android

har2008preet commented 4 years ago
useScrollView={true}
firstItem={data.length}
initialScrollIndex={data.length}
getItemLayout={(data, index) => (
          {length: width, offset: width * index, index}
)}
horizontal={true}
loop={true}
loopClonesPerSide={data.length}
data={data.banners}

This worked for me

bd-arc commented 4 years ago

@har2008preet ⚠️ I strongly advise against that because you're basically rendering thrice your data set. Meaning if you have 10 items in your data prop, you're now rendering 30 items all at once!

har2008preet commented 4 years ago

@bd-arc noted. Will look for other option.

Ericky14 commented 4 years ago

@har2008preet Have you found another option for infinite loop?

TomJKlug commented 4 years ago

Loop does not work in android. It either gets stuck or does some really odd index hopping resulting in some insane visuals.

Yaolegol commented 4 years ago

same issue

qiaoyixuan commented 4 years ago

same issue

har2008preet commented 4 years ago
initialScrollIndex={this.props.store.banners.length-3}
                    firstItem={this.props.store.banners.length-3}
                    data={this.props.store.banners}

Working for me, without any overhead. How's this for a hack? @bd-arc 😄

qiaoyixuan commented 4 years ago

what does the number 3 mean?

发自我的iPhone

在 2020年2月10日,16:23,Harpreet Singh notifications@github.com 写道:

 initialScrollIndex={this.props.store.banners.length-3} firstItem={this.props.store.banners.length-3} data={this.props.store.banners} Working for me, without any overhead. How's this for a hack? @bd-arc 😄

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

har2008preet commented 4 years ago

3 is the default loopClonePerSide value

alessandrofc commented 4 years ago

Same problem in Android

Leehee1on commented 4 years ago

Same problem in Android

ghost commented 4 years ago

Same problem in Android

ThatMate commented 4 years ago

I have similar experience. Loop works inconsistently.

  1. It stops at the end of the last item
  2. When you scroll back in the reverse direction after reaching to last item as in step # 1, the loop looks like working in both the direction

I think we need some attention to this issue.

ThatMate commented 4 years ago

I have created a video showing the inconsistency in loop behavior:

Inconsistent Looping

ThatMate commented 4 years ago

I think I might be overreacting, it maybe just the standard issue in emulator as mentioned here: https://github.com/archriss/react-native-snap-carousel#important-note-regarding-android

I will have a check. Sorry for the noise.

har2008preet commented 4 years ago

@ThatMate it might fix in the future release #678

Tejaswini-PD commented 4 years ago

@ThatMate I am facing the same issue where the scroll stops to the right at 3rd slide during second loop. When we scroll to left, infinite scroll is working properly. Have you found any fix for it ?

JoaoCEAlmeida commented 4 years ago

@Tejaswini-PD I'm having the same problem as you. Did you find any work around?

Thanks in advance.

Tejaswini-PD commented 4 years ago

@JoaoCEAlmeida In line number ~820 in Carousel.js file, we have this code -

if (nextActiveItem === this._itemToSnapTo && scrollOffset === this._scrollOffsetRef) { this._repositionScroll(nextActiveItem); }

So scrollOffset and this._scrollOffsetRef didn't have same numbers after the decimal. Not sure what causes this. But truncating the decimal part of it before checking equality fixed this issue for me - Math.trunc(scrollOffset) === Math.trunc(this._scrollOffsetRef).

Hope this helps !

JoaoCEAlmeida commented 4 years ago

@Tejaswini-PD still doesn't work, the behavior is the same. But thanks for the help.

Can you share the props that you're passing to your carousel?

Tejaswini-PD commented 4 years ago

@JoaoCEAlmeida These are the optional props I am using. Data, renderItem, etc are anyways needed. layout={‘default’} sliderWidth={SCREEN_WIDTH} itemWidth={SCREEN_WIDTH} loop autoplay inactiveSlideOpacity={1} inactiveSlideScale={1}

JoaoCEAlmeida commented 4 years ago

@Tejaswini-PD what version are you currently using?

Tejaswini-PD commented 4 years ago

@JoaoCEAlmeida I am using version 3.9.1

JoaoCEAlmeida commented 4 years ago

@Tejaswini-PD Can't make it work, but thanks for the help.

JoaoCEAlmeida commented 4 years ago

+1

JoaoCEAlmeida commented 4 years ago

@ThatMate Did it work for you? I've tested in a real device and still does not work.

Thanks in advance.

JoaoCEAlmeida commented 4 years ago

@Tejaswini-PD how many items are you showing on the screen? I'm showing 4. Can you test your version with 4 items? You just need to divide "itemWidth" prop by 4. image

BertoGz commented 3 years ago

hey guys I just noticed something. Im using the carousel in two places in my app. for some reason in my main menu it loops! but not in the other menu! ( glitches out ) I will check out why this is. ( could be styling reasons ). i'll update you on this.

update: ah pooey so in the main menu in only loops twice going forwards. but infinitely going backwards??

Saad9624 commented 3 years ago

comment out this line under Crousel.js return this._carouselRef && this._carouselRef.getNode && this._carouselRef.getNode();

Thanks me later!

Saad9624 commented 3 years ago

@JoaoCEAlmeida In line number ~820 in Carousel.js file, we have this code -

if (nextActiveItem === this._itemToSnapTo && scrollOffset === this._scrollOffsetRef) { this._repositionScroll(nextActiveItem); }

So scrollOffset and this._scrollOffsetRef didn't have same numbers after the decimal. Not sure what causes this. But truncating the decimal part of it before checking equality fixed this issue for me - Math.trunc(scrollOffset) === Math.trunc(this._scrollOffsetRef).

Hope this helps !

not working on android

omaryoussef commented 3 years ago

This might be a very stupid solution but it seems to work for me. I'm using version 4.0.0-beta.6 of the package. I'm also using the useScrollView={true} property on the carousel:

<Carousel
          ref={(c) => (this._slider1Ref = c)}
          data={banners}
          vertical={false}
          renderItem={this._renderItem}
          sliderWidth={sliderWidth}
          itemWidth={itemWidth}
          hasParallaxImages={false}
          firstItem={SLIDER_1_FIRST_ITEM}
          inactiveSlideScale={0.94}
          inactiveSlideOpacity={1}
          // inactiveSlideShift={20}
          containerCustomStyle={styles.slider}
          contentContainerCustomStyle={styles.sliderContentContainer}
          loop={true}
          useScrollView={true}
          loopClonesPerSide={banners.length}
          autoplay={true}
          autoplayDelay={100}
          autoplayInterval={2000}
          enableSnap={true}
          // enableMomentum={true}
          onSnapToItem={this._onSnapToItem}
        />

I changed the duration of the timeout from 400 to 200 when the reposition happens on Android here in Carousel.tsx at line 941:

if (IS_ANDROID && this._shouldRepositionScroll(index)) {
        if (animated) {
          this._androidRepositioningTimeout = setTimeout(() => {
            // Without scroll animation, the behavior is completely buggy...
            this._repositionScroll(index, true);
          }, 200); // Changed from 400 to 200.
        }
 } 

And that seems to fix the weird flipping animation you get when it goes to the end.

I was able to extend the Carousel class in my code and overwrite the method _snapToItem with the change.

anupamhore commented 3 years ago

@omaryoussef what have you done on overwriting this _snapToItem... I am not able to make it work on Android as well. Not even applying any of the above logics

omaryoussef commented 3 years ago

@anupamhore I basically created a new component out of the base CarouselBase component this library uses:

import { IS_ANDROID } from 'common/App';
import CarouselBase from 'react-native-snap-carousel';

/**
 * @inheritdoc
 */
export default class Carousel<T> extends CarouselBase<T> {
  _snapToItem(index: number, animated = true, fireCallback = true, forceScrollTo = false) {
    const { onSnapToItem } = this.props;
    const itemsLength = this._getCustomDataLength();
    const wrappedRef = this._getWrappedRef();

    if (!itemsLength || !wrappedRef) {
      return;
    }

    if (!index || index < 0) {
      index = 0;
    } else if (itemsLength > 0 && index >= itemsLength) {
      index = itemsLength - 1;
    }

    if (index === this._activeItem && !forceScrollTo) {
      return;
    }

    const offset = this._getItemScrollOffset(index);

    if (offset === undefined) {
      return;
    }

    this._scrollTo({ offset, animated });

    // On both platforms, `onMomentumScrollEnd` won't be triggered if the scroll isn't animated
    // so we need to trigger the callback manually
    // On Android `onMomentumScrollEnd` won't be triggered when scrolling programmatically
    // Therefore everything critical needs to be manually called here as well, even though the timing might be off
    const requiresManualTrigger = !animated || IS_ANDROID;
    if (requiresManualTrigger) {
      this._activeItem = index;

      if (fireCallback) {
        onSnapToItem && onSnapToItem(this._getDataIndex(index));
      }

      // Repositioning on Android
      if (IS_ANDROID && this._shouldRepositionScroll(index)) {
        if (animated) {
          this._androidRepositioningTimeout = setTimeout(() => {
            // Without scroll animation, the behavior is completely buggy...
            this._repositionScroll(index, true);
          }, 200); // Approximate scroll duration on Android
        } else {
          this._repositionScroll(index);
        }
      }
    }
  }
}

IS_ANDROID is just a constant checking if the current platform is Android in my own business logic.

drakyone commented 3 years ago

there's no any methods like _shouldRepositionScroll or _getItemScrollOffset what kind of version do you using?

brambang commented 2 years ago

comment out this line under Crousel.js return this._carouselRef && this._carouselRef.getNode && this._carouselRef.getNode();

Thanks me later!

did it work for you? is there anyone tried this?

dohooo commented 2 years ago

Sorry, please allow me to advertise for my open source library! ~ I think this library react-native-reanimated-carousel will solve your problem. It is a high performance and very simple component, complete with React-Native reanimated 2

bandhavya commented 2 years ago

Same issue using 3.9.1 version. Any one found the solution to this ?

patissier-boulanger commented 2 years ago

4.0.0beta6 probleme still exist..

alextyshchenko commented 2 years ago

Hi all, I have solved that issue in my app. sliderWidth and itemWidth must be integers.

<Carousel loop={true} data={products} renderItem={renderItem} inactiveSlideScale={1} inactiveSlideOpacity={1} sliderWidth={Math.round(deviceWidth)} itemWidth={Math.round(deviceWidth * 0.37)} />

linxianxi commented 2 years ago

大家好, 我已经在我的应用程序中解决了这个问题。 sliderWidth 和 itemWidth 必须是整数。

<Carousel loop={true} data={products} renderItem={renderItem} inactiveSlideScale={1} inactiveSlideOpacity={1} sliderWidth={Math.round(deviceWidth)} itemWidth={Math.round(deviceWidth * 0.37)} />

quick swipe will not work

JeanSilvany commented 1 year ago

Hi all, I have solved that issue in my app. sliderWidth and itemWidth must be integers.

<Carousel loop={true} data={products} renderItem={renderItem} inactiveSlideScale={1} inactiveSlideOpacity={1} sliderWidth={Math.round(deviceWidth)} itemWidth={Math.round(deviceWidth * 0.37)} />

Its works on Android and iOS, thank you!

vasylnahuliak commented 11 months ago

JFYI: interesting solution https://youtu.be/WTfZJ9TreFc?si=X0WkniduEML5bxQM&t=752

https://github.com/animate-react-native/marquee