React-Multi-Carousel无限模式下slideIndex异常及居中放大轮播问题
解决react-multi-carousel无限轮播下索引异常问题并实现中间项放大效果
问题根源
开启infinite模式后,react-multi-carousel会自动在原始轮播项的首尾添加重复项以实现无缝滚动,导致beforeChange回调返回的nextSlide/current索引是基于扩展后的数组,而非原始数据的真实索引,进而无法正确控制轮播项的样式逻辑。
核心解决方案:计算真实索引
通过取模运算将扩展数组的索引转换为原始数据的真实索引,同时处理向左滚动到复制项时的负数索引边界情况:
- 先获取原始轮播项的总长度:
const itemCount = data?.banners?.length || 0;
- 编写索引转换函数:
const getRealIndex = (index, totalItems) => { if (totalItems === 0) return 0; let realIndex = index % totalItems; // 处理向左滚动到复制项时的负数索引 return realIndex < 0 ? realIndex + totalItems : realIndex; };
- 修改
beforeChange回调,存储真实索引:
beforeChange={(nextSlide, current) => { const realNextIndex = getRealIndex(nextSlide, itemCount); setSlideIndex(realNextIndex); }}
实现中间激活项放大效果
结合centerMode和自定义样式,实现示例图的中间项突出效果:
- 开启
centerMode并调整响应式配置:
const responsiveCarouselTournamentBanner = { mobile: { breakpoint: { max: 464, min: 0 }, items: 1.5 // 移动端显示1个完整项+左右各半个项 }, tablet: { breakpoint: { max: 768, min: 465 }, items: 2.5 }, desktop: { breakpoint: { max: Infinity, min: 769 }, items: 3 // 桌面端显示3个项,中间为激活项 } }; // 在Carousel组件中开启centerMode <Carousel // ...其他属性 centerMode={true} // ... >
- 给激活项添加特殊样式:
修改轮播项的className,根据真实索引判断是否为激活项:
{data?.banners?.map((item, index) => ( <img key={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 ${slideIndex === index ? 'active-slide' : ''}`} alt="dgg-banner" /> ))}
- 添加CSS样式:
/* 轮播项容器基础样式 */ .custom-item-class { display: flex; justify-content: center; align-items: center; transition: transform 0.3s ease; } /* 激活项放大 */ .active-slide { transform: scale(1.1); z-index: 1; } /* 非激活项缩小 */ .custom-item-class:not(.active-slide) { transform: scale(0.9); opacity: 0.8; }
完整代码示例
import Carousel from 'react-multi-carousel'; import 'react-multi-carousel/lib/styles.css'; import { useState } from 'react'; const TournamentBanner = ({ data }) => { const [slideIndex, setSlideIndex] = useState(0); const itemCount = data?.banners?.length || 0; const responsiveCarouselTournamentBanner = { mobile: { breakpoint: { max: 464, min: 0 }, items: 1.5 }, tablet: { breakpoint: { max: 768, min: 465 }, items: 2.5 }, desktop: { breakpoint: { max: Infinity, min: 769 }, items: 3 } }; const getRealIndex = (index, totalItems) => { if (totalItems === 0) return 0; let realIndex = index % totalItems; return realIndex < 0 ? realIndex + totalItems : realIndex; }; return ( <Carousel className="relative" responsive={responsiveCarouselTournamentBanner} autoPlaySpeed={3000} infinite arrows={false} itemClass="custom-item-class" centerMode={true} slidesToSlide={1} beforeChange={(nextSlide, current) => { const realNextIndex = getRealIndex(nextSlide, itemCount); setSlideIndex(realNextIndex); }} > {data?.banners?.map((item, index) => ( <img key={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 ${slideIndex === index ? 'active-slide' : ''}`} alt="dgg-banner" /> ))} </Carousel> ); }; export default TournamentBanner;
内容的提问来源于stack exchange,提问作者Mohtep
相关产品推荐
相关产品推荐

