Data-index of React-Multi-Carousel in infinity mode is not true

430 Views Asked by At

I want to make carousel slider with middle item (active item) is bigger than other. Like this image below enter image description here

So i use callback function of react-multi-carousel

<Carousel
              className="relative"
              responsive={responsiveCarouselTournamentBanner}
              // autoPlay={true}
              autoPlaySpeed={3000}
              infinite
              arrows={false}
              itemClass="custom-item-class"
              // centerMode={true}
              slidesToSlide={1}
              beforeChange={(nextSlide, current) => {
                console.log("Before Change: ", nextSlide, "Current: ", current)
                setSlideIndex(nextSlide)
              }}
              // containerClass={`${
              //   slideIndex % 2 === 0 ? "custom-container-odd" : "custom-container-even"
              // }`}
            >
              {data?.banners?.map((item, index) => (
                <img
                  key={index}
                  data-index={index}
                  src={item || "/images/tournament-img-default.png"}
                  className="w-full max-w-[90%] h-[210px] md:max-w-[858px] md:h-[482px] object-cover mx-auto rounded-xl banner-primary-color"
                  alt="dgg-banner"
                />
                // <div key={index}>ABC</div>
              ))}
            </Carousel>

Like this. I want to use slideIndex from nextSlide,current in beforeChange. But with Infinity mode, totalItems will double. And i cannot control my items in carousel.

So i want to ask my question to handle this case of carousel like this one. Many thanks. I struggle with this issue for a week.

2

There are 2 best solutions below

3
Narendra Bisht On

To create a carousel/slider with a larger active item in the middle, you can modify your code to achieve the desired effect. Here's an example of how you can approach it:

First, define the responsive settings for the carousel:

const responsiveCarouselTournamentBanner = {
  superLargeDesktop: {
    breakpoint: { max: 4000, min: 3000 },
    items: 5,
  },
  desktop: {
    breakpoint: { max: 3000, min: 1024 },
    items: 3,
  },
  tablet: {
    breakpoint: { max: 1024, min: 464 },
    items: 2,
  },
  mobile: {
    breakpoint: { max: 464, min: 0 },
    items: 1,
  },
};

Then, in your JSX code, use the beforeChange callback to update the active slide index and adjust the styles based on the active slide:

<Carousel
  className="relative"
  responsive={responsiveCarouselTournamentBanner}
  autoPlay={true}
  autoPlaySpeed={3000}
  infinite
  arrows={false}
  itemClass="custom-item-class"
  centerMode
  centerSlidePercentage={80}
  slidesToSlide={1}
  beforeChange={(nextSlide, currentSlide) => {
    setSlideIndex(nextSlide);
  }}
>
  {data?.banners?.map((item, index) => (
    <div
      key={index}
      className={`${
        slideIndex === index ? 'active-slide' : ''
      } custom-slide`}
    >
      <img
        src={item || '/images/tournament-img-default.png'}
        className="w-full h-[210px] md:h-[482px] object-cover mx-auto rounded-xl banner-primary-color"
        alt="dgg-banner"
      />
    </div>
  ))}
</Carousel>

You can add CSS styles to the active-slide and custom-slide classes to control the appearance of the active and non-active slides.

Make sure to adjust the code and styles according to your specific requirements and design.

1
Tiber On

I had the same issue here (GitHub)!

To solve this I made a function that resolves the active slide (changeSlide). It works only if the carousel it's set to ltr (which it's the default) and infinite & centerMode are true. I'm using Tailwind so you probably want to check it out too.

const responsive = {
    superLargeDesktop: {
        // the naming can be any, depends on you.
        breakpoint: { max: 4000, min: 3000 },
        items: 1
    },
    desktop: {
        breakpoint: { max: 3000, min: 1024 },
        items: 1
    },
    tablet: {
        breakpoint: { max: 1024, min: 464 },
        items: 1
    },
    mobile: {
        breakpoint: { max: 464, min: 0 },
        items: 1
    }
};
const [activeIndex, setActiveIndex] = useState(0)
const images = [
    {
        src: slide1,
        alt: "",
    },
    {
        src: slide2,
        alt: "",
    },
    {
        src: slide3,
        alt: "",
    },
    {
        src: slide4,
        alt: "",
    },
]
const changeSlide = (previousSlide: number, currentSlide: number, dataSize: number) => {
    let activeSlide = 0
    // right arrow
    if (previousSlide < currentSlide) activeSlide = currentSlide - 2 === dataSize ? 0 : currentSlide - 2
    // left arrow
    else activeSlide = currentSlide + (currentSlide <= dataSize && currentSlide >= 2 ? -2 : dataSize - 2);
    setActiveIndex(activeSlide)
}

And the carousel should be like this:

<Carousel
    infinite
    autoPlay
    centerMode
    responsive={responsive}
    className="w-full h-[70%] owl-carousel owl-theme text-center"
    itemClass="px-1 flex justify-center"
    keyBoardControl={true}
    // changeSlide(previousSlide, currentSlide, dataSize = images.length)
    afterChange={(previousSlide, { currentSlide }) => changeSlide(previousSlide, currentSlide, images.length)}
>
    {images.map((img, i) => (
        <div
            key={`slide-${i}`}
            className={`relative ${activeIndex !== i ? "w-[90%]" : "w-full"} h-full rounded-[10px] flex justify-center items-center bg-transparent shadow-[10px_40px_10px_5px_black]`}
        >
            {activeIndex !== i ?
                <img
                    className='pointer-events-none object-contain rounded-[10px] opacity-10'
                    src={img.src}
                    alt={img.alt}
                />
                :
                <img
                    key={`slide-img-${activeIndex}`}
                    className='object-contain rounded-[10px]'
                    src={img.src}
                    alt={img.alt}
                />
            }
        </div>
    )
    )}
</Carousel>

I hope this helps!