React Native Carousel With Pagination, Auto Play and Infinite Loop Fully Customisable

I will walk you through a React Native CarouselComponent that handles dynamic data and autoplay functionality. This component also incorporates smooth pagination, user interactions, and automatic scrolling.

Key Features:

Auto-scroll with …


This content originally appeared on DEV Community and was authored by Ajmal Hasan

Image description
I will walk you through a React Native CarouselComponent that handles dynamic data and autoplay functionality. This component also incorporates smooth pagination, user interactions, and automatic scrolling.

Key Features:

  • Auto-scroll with intervals and dynamic offset control
  • Pagination that reflects the active slide
  • Manual interruption of auto-scroll by user interaction
  • Dynamic styling for each carousel item

The Component Breakdown

1. Core Libraries

We are leveraging several key libraries to build this carousel:

  • react-native-reanimated: Provides performant animations and scroll handling.
  • react-native: Provides core components like FlatList, StyleSheet, and View.

2. Shared State and Scroll Handling

The component uses shared values (useSharedValue) for tracking the scroll offset (x and offset). useAnimatedScrollHandler is employed to update the x value dynamically as the user scrolls through the carousel.

const x = useSharedValue(0);
const offset = useSharedValue(0);

const onScroll = useAnimatedScrollHandler({
  onScroll: (e) => {
    x.value = e.contentOffset.x;
  },
  onMomentumEnd: (e) => {
    offset.value = e.contentOffset.x;
  },
});

3. Autoplay Functionality

The carousel automatically scrolls through items with an interval. If the user interacts with the carousel by swiping, the autoplay is paused, and it resumes once scrolling ends.

useEffect(() => {
  if (isAutoPlay) {
    interval.current = setInterval(() => {
      if (offset.value >= (dataList.length - 1) * width) {
        offset.value = 0; // Loop back to the start
      } else {
        offset.value += width;
      }
    }, 1000);
  } else {
    clearInterval(interval.current);
  }
  return () => clearInterval(interval.current);
}, [isAutoPlay, offset, width, dataList.length]);

4. Pagination

Pagination is a simple set of dots that updates based on the current item being viewed. The active dot gets a more prominent style.

const Pagination = ({ data, paginationIndex }) => {
  return (
    <View style={styles.paginationContainer}>
      {data.map((_, index) => (
        <View key={index} style={paginationIndex === index ? styles.activeDot : styles.inactiveDot} />
      ))}
    </View>
  );
};

5. Dynamic Carousel Item Rendering

Each carousel item is rendered within a custom AnimatedScalePressView component that allows scaling when pressed. We also give each item a random background color for a visually distinct appearance.

const renderItem = ({ item, index }) => (
  <TouchableOpacity key={index} onPress={() => onPressItem(item)}>
    <View
      style={{
        padding: 2,
        height: 150,  // Item height value
        backgroundColor: getRandomColor(),
        width: width - 30,  // Item width value
        marginHorizontal: 15,  //item margin
        borderRadius: 10,  // Static border radius
      }}
    />

  </TouchableOpacity>
);

Complete Code

import { StyleSheet, View, useWindowDimensions, TouchableOpacity } from 'react-native';
import React, { useEffect, useRef, useState } from 'react';
import Animated, { scrollTo, useAnimatedRef, useAnimatedScrollHandler, useDerivedValue, useSharedValue } from 'react-native-reanimated';
import AnimatedScalePressView from '../../AnimatedScalePressView';

/*
Usage:
< CarouselComponent data={list||[]} autoPlay onPressItem={onPressItem} />
*/

const Dot = ({ index, paginationIndex }) => {
  return <View style={paginationIndex === index ? styles.activeDot : styles.inactiveDot} />;
};

const Pagination = ({ data, paginationIndex }) => {
  return (
    <View style={styles.paginationContainer}>
      {data.map((_, index) => (
        <Dot index={index} key={index} paginationIndex={paginationIndex} />
      ))}
    </View>
  );
};

const CarouselComponent = ({ data: dataList = [], autoPlay, onPressItem }) => {
  const x = useSharedValue(0);
  const { width } = useWindowDimensions();
  const ref = useAnimatedRef();
  const [currentIndex, setCurrentIndex] = useState(0);
  const [paginationIndex, setPaginationIndex] = useState(0);
  const [isAutoPlay, setIsAutoPlay] = useState(autoPlay);
  const offset = useSharedValue(0);
  const interval = useRef();

  const [data, setData] = useState([...dataList]);

  const viewabilityConfig = {
    itemVisiblePercentThreshold: 50,
  };

  const onViewableItemsChanged = ({ viewableItems }) => {
    if (viewableItems[0] && viewableItems[0].index !== null) {
      setCurrentIndex(viewableItems[0].index);
      setPaginationIndex(viewableItems[0].index % dataList.length);
    }
  };

  const viewabilityConfigCallbackPairs = useRef([{ viewabilityConfig, onViewableItemsChanged }]);

  const onScroll = useAnimatedScrollHandler({
    onScroll: (e) => {
      x.value = e.contentOffset.x;
    },
    onMomentumEnd: (e) => {
      offset.value = e.contentOffset.x;
    },
  });

  useDerivedValue(() => {
    scrollTo(ref, offset.value, 0, true);
  });

  useEffect(() => {
    if (isAutoPlay) {
      interval.current = setInterval(() => {
        if (offset.value >= (dataList.length - 1) * width) {
          offset.value = 0;
        } else {
          offset.value += width;
        }
      }, 1000);
    } else {
      clearInterval(interval.current);
    }
    return () => clearInterval(interval.current);
  }, [isAutoPlay, offset, width, dataList.length]);

  const getRandomColor = () => {
    const letters = '0123456789ABCDEF';
    let color = '#';
    for (let i = 0; i < 6; i++) {
      color += letters[Math.floor(Math.random() * 16)];
    }
    return color;
  };

  const renderItem = ({ item, index }) => (
    <AnimatedScalePressView key={index} onPress={() => onPressItem(item)}>
      <View
        style={{
          padding: 2,
          height: 150,
          backgroundColor: getRandomColor(),
          width: width - 30,
          marginHorizontal: 15,
          borderRadius: 10,
        }}
      />
    </AnimatedScalePressView>
  );

  return (
    <View style={styles.container}>
      <Animated.FlatList
        ref={ref}
        style={{ flexGrow: 0 }}
        data={data}
        horizontal
        pagingEnabled
        bounces={false}
        onScroll={onScroll}
        scrollEventThrottle={16}
        showsHorizontalScrollIndicator={false}
        onScrollBeginDrag={() => setIsAutoPlay(false)}
        onMomentumScrollEnd={() => setIsAutoPlay(true)}
        viewabilityConfigCallbackPairs={viewabilityConfigCallbackPairs.current}
        onEndReached={() => {
          if (data.length === dataList.length * 2) return;
          setData((prevData) => [...prevData, ...dataList]);
        }}
        onEndReachedThreshold={0.5}
        keyExtractor={(_, index) => `carousel_item_${index}`}
        renderItem={renderItem}
        removeClippedSubviews
      />
      {dataList.length > 0 && <Pagination paginationIndex={paginationIndex} data={dataList} />}
    </View>
  );
};

export default CarouselComponent;

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  paginationContainer: {
    flexDirection: 'row',
    height: 30, // gap between pagination dots and list
    justifyContent: 'center',
    alignItems: 'center',
  },
  activeDot: {
    backgroundColor: '#800020', // Burgundy
    height: 8,
    width: 30,
    marginHorizontal: 2,
    borderRadius: 12,
  },
  inactiveDot: {
    backgroundColor: '#D3D3D3', // Light grey
    height: 8,
    width: 8,
    marginHorizontal: 2,
    borderRadius: 12,
  },
});

Conclusion

This carousel component can easily fit into various React Native projects, providing a smooth scrolling experience with pagination and autoplay.


This content originally appeared on DEV Community and was authored by Ajmal Hasan


Print Share Comment Cite Upload Translate Updates
APA

Ajmal Hasan | Sciencx (2024-09-19T16:40:59+00:00) React Native Carousel With Pagination, Auto Play and Infinite Loop Fully Customisable. Retrieved from https://www.scien.cx/2024/09/19/react-native-carousel-with-pagination-auto-play-and-infinite-loop-fully-customisable/

MLA
" » React Native Carousel With Pagination, Auto Play and Infinite Loop Fully Customisable." Ajmal Hasan | Sciencx - Thursday September 19, 2024, https://www.scien.cx/2024/09/19/react-native-carousel-with-pagination-auto-play-and-infinite-loop-fully-customisable/
HARVARD
Ajmal Hasan | Sciencx Thursday September 19, 2024 » React Native Carousel With Pagination, Auto Play and Infinite Loop Fully Customisable., viewed ,<https://www.scien.cx/2024/09/19/react-native-carousel-with-pagination-auto-play-and-infinite-loop-fully-customisable/>
VANCOUVER
Ajmal Hasan | Sciencx - » React Native Carousel With Pagination, Auto Play and Infinite Loop Fully Customisable. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2024/09/19/react-native-carousel-with-pagination-auto-play-and-infinite-loop-fully-customisable/
CHICAGO
" » React Native Carousel With Pagination, Auto Play and Infinite Loop Fully Customisable." Ajmal Hasan | Sciencx - Accessed . https://www.scien.cx/2024/09/19/react-native-carousel-with-pagination-auto-play-and-infinite-loop-fully-customisable/
IEEE
" » React Native Carousel With Pagination, Auto Play and Infinite Loop Fully Customisable." Ajmal Hasan | Sciencx [Online]. Available: https://www.scien.cx/2024/09/19/react-native-carousel-with-pagination-auto-play-and-infinite-loop-fully-customisable/. [Accessed: ]
rf:citation
» React Native Carousel With Pagination, Auto Play and Infinite Loop Fully Customisable | Ajmal Hasan | Sciencx | https://www.scien.cx/2024/09/19/react-native-carousel-with-pagination-auto-play-and-infinite-loop-fully-customisable/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.