callstack / react-native-pager-view

React Native wrapper for the Android ViewPager and iOS UIPageViewController.
MIT License
2.75k stars 422 forks source link

infinite scroll #196

Open hengkx opened 4 years ago

hengkx commented 4 years ago

As dev, I would like to have ability to have infinite scroll behavior.

troZee commented 4 years ago

Hey, For now, we don't support this kind of functionality. PR are welcome.

hengkx commented 4 years ago

30

dhirajanand014 commented 4 years ago

How do we about implementing it customly. This is much required!

binchik commented 3 years ago

I insert the first page to the end of pages aray: const pages = [<Page1 />, <Page2 />, <Page3 />, <Page1 />];. Then I track selected page using onPageSelected callback, and when selectedPage >= pages.length - 1 I call pagerViewRef.current?.setPageWithoutAnimation(0);.

That creates an illusion of infinite scroll, it doesn't work if you want to go swipe back from the first page to see the last, but it must be achievable in the same way, setting the initialPage={1}.

A proper implementation would be nice though.

ahmedam55 commented 3 years ago

To continue of @binchik steps:

const slides = [slide1, slide2, slide3]
  const firstSlide = slides[0]
  const lastSlide = slides[slides.length - 1]
  const loopingSlides = [lastSlide, ...slides, firstSlide]
  const viewPagerRef = useRef()

  return (
    <>
      <ViewPager
        initialPage={1}
        ref={viewPagerRef}
        onPageSelected={event => {
          const currentPage = event.nativeEvent.position
          const reachedFakeLastSlide = currentPage === 0
          const reachedFakeFirstSlide = currentPage === loopingSlides.length - 1

          if (reachedFakeFirstSlide) {
            viewPagerRef.current.setPageWithoutAnimation(1)
          } else if (reachedFakeLastSlide) {
            viewPagerRef.current.setPageWithoutAnimation(loopingSlides.length - 2)
          } else {
            setPage(currentPage)
          }
        }}
        style={styles.container}
      >
        {loopingSlides}
      </ViewPager>
      <Indicators length={3} active={page - 1} />
    </>
  )
oarsoy commented 3 years ago

To continue of @binchik steps:

const slides = [slide1, slide2, slide3]
  const firstSlide = slides[0]
  const lastSlide = slides[slides.length - 1]
  const loopingSlides = [lastSlide, ...slides, firstSlide]
  const viewPagerRef = useRef()

  return (
    <>
      <ViewPager
        initialPage={1}
        ref={viewPagerRef}
        onPageSelected={event => {
          const currentPage = event.nativeEvent.position
          const reachedFakeLastSlide = currentPage === 0
          const reachedFakeFirstSlide = currentPage === loopingSlides.length - 1

          if (reachedFakeFirstSlide) {
            viewPagerRef.current.setPageWithoutAnimation(1)
          } else if (reachedFakeLastSlide) {
            viewPagerRef.current.setPageWithoutAnimation(loopingSlides.length - 2)
          } else {
            setPage(currentPage)
          }
        }}
        style={styles.container}
      >
        {loopingSlides}
      </ViewPager>
      <Indicators length={3} active={page - 1} />
    </>
  )

There is no example for loop implementation with this component. I tried to apply this code to my project, but I couldn't run. Any idea?

  render() {

  const slides = this.state.slideData;
  const viewPagerRef = useRef<Container2 | null>(null);
    const firstSlide = slides[0]
  const lastSlide = slides[slides.length - 1]
  const loopingSlides = [lastSlide, ...slides, firstSlide]
const Container2 = styled(PagerView)`
    height: ${height}px;
    width: 100%;
`
<Container2
            orientation='horizontal'
            initialPage={1}
            onPageSelected={(e) => {

 const currentPage = e.nativeEvent.position
          const reachedFakeLastSlide = currentPage === 0
          const reachedFakeFirstSlide = currentPage === loopingSlides.length - 1

          if (reachedFakeFirstSlide) {
            viewPagerRef.current.setPageWithoutAnimation(1)
          } else if (reachedFakeLastSlide) {
            viewPagerRef.current.setPageWithoutAnimation(loopingSlides.length - 2)
          }

    this.setState({ selectedd: e.nativeEvent.position })
  //  console.log(this.state.selectedd);
   // alert('aaas') 
  }
  }
        ref={viewPagerRef}>                 
{item.subdata.map((subitem, indexx) => {
                return (
<View key={indexx} style={{ justifyContent: 'center', width: '100%', }}>                    
CPPAlien commented 3 years ago

@binchik it's not good, it will cause blink

troZee commented 3 years ago

I did a investigation and it should be implemented using below tutorials/code:

Android: https://medium.com/mobile-app-development-publication/android-bi-direction-infinite-viewpager-2-scrolling-1a729e4ee773

iOS: https://github.com/igroomgrim/Infinite-scroll-with-uipageviewcontroller

SymntxHomendra51 commented 2 years ago

Those who are looking for infinite loop function. Check this https://github.com/computerjazz/react-native-infinite-pager.

SumitLubal commented 1 year ago

This is super important and needed solution for our app as well, any help from be super appreciated

nicolaosm commented 10 months ago

Still can't use this library without infinite scroll :(

thcolin commented 2 weeks ago

A solution

To summarize (without considering specific cases):

const VirtualPagerView = ({
  onPageSelected,
  initialPage = 0,
  pages,
  pageComponent: PageComponent,
  ...props
}) => {
  const ref = useRef()
  const [offset, setOffset] = useState(initialPage)

  return (
    <PagerView
      {...props}
      initialPage={initialPage}
      ref={(pager) => {
        ref.current = {
          ...pager,
          setPage: (page) => {
            setOffset(page ? page + 1 : 0)
            pager.setPageWithoutAnimation(0)
          }
        }
      }}
      onPageSelected={e => {
        const position = e.nativeEvent.position

        if (position === 1) {
          if (offset === 0) {
            setOffset(1)

            if (typeof onPageSelected === 'function') {
              onPageSelected(1)
            }
          }
          return
        }

        setOffset(value => {
          const offset = Math.max(0, position ? value + 1 : value - 1)

          if (offset) {
            ref.current.setPageWithoutAnimation(1)
          }

          if (typeof onPageSelected === 'function') {
            onPageSelected(offset)
          }

          return offset
        })
      }}
    >
      {pages.slice(Math.max(0, offset - 1), Math.max(3, offset + 2)).map((page, index) => (
        <PageComponent key={page.key} {...page} />
      ))}
    </PagerView>
  )
})

Usage

<VirtualPagerView
  onPageSelected={position => {
    console.log(`Page ${position} selected`)
  }}
  pages={[{ key: 1, name: 'A' }, { key: 2, name: 'B' }, { key: 3, name: 'C' }, { key: 4, name: 'D' }, { key: 5, name: 'E' }, { key: 6, name: 'F' }]}
  pageComponent={({ name }) => (
    <View>
      <Text>{name}</Text>
    </View>
  )}
/>