React实现无回滚无限轮播组件(不依赖第三方库)
React无回滚无限轮播组件实现(无第三方库)
核心思路
要实现无回滚的无缝轮播,核心是视觉欺骗+DOM重置:
- 给原始轮播列表首尾各复制一份对方的元素(比如原列表
[1,2,3,4]处理为[4,1,2,3,4,1]) - 正常滚动时用CSS过渡实现平滑滑动;当滚动到复制的边界元素时,瞬间重置到真实列表的对应位置(同时关闭过渡动画),用户完全感知不到跳变
完整代码实现
import { useState, useRef, useEffect } from 'react'; const InfiniteCarousel = ({ items }) => { // 处理后的轮播列表:首尾各加一份对方的元素 const carouselItems = [items[items.length - 1], ...items, items[0]]; const [currentIndex, setCurrentIndex] = useState(1); // 初始从真实列表第一个元素开始 const containerRef = useRef(null); const itemRef = useRef(null); let itemWidth = 0; // 获取单个轮播项的宽度 useEffect(() => { if (itemRef.current) { itemWidth = itemRef.current.offsetWidth; } }, []); // 处理过渡结束后的索引重置 const handleTransitionEnd = () => { if (currentIndex === 0) { // 滚动到开头的复制元素,重置到真实列表最后一个 setCurrentIndex(items.length); // 关闭过渡,瞬间跳转 containerRef.current.style.transition = 'none'; containerRef.current.style.transform = `translateX(-${items.length * itemWidth}px)`; // 在下一帧重新开启过渡 requestAnimationFrame(() => { containerRef.current.style.transition = 'transform 0.5s ease'; }); } else if (currentIndex === carouselItems.length - 1) { // 滚动到结尾的复制元素,重置到真实列表第一个 setCurrentIndex(1); containerRef.current.style.transition = 'none'; containerRef.current.style.transform = `translateX(-${1 * itemWidth}px)`; requestAnimationFrame(() => { containerRef.current.style.transition = 'transform 0.5s ease'; }); } }; // 向右切换 const handleNext = () => { setCurrentIndex(prev => prev + 1); }; // 向左切换 const handlePrev = () => { setCurrentIndex(prev => prev - 1); }; return ( <div style={{ width: '600px', margin: '0 auto', overflow: 'hidden', position: 'relative' }}> <div ref={containerRef} style={{ display: 'flex', transform: `translateX(-${currentIndex * itemWidth}px)`, transition: 'transform 0.5s ease', }} onTransitionEnd={handleTransitionEnd} > {carouselItems.map((item, idx) => ( <div key={idx} ref={idx === 1 ? itemRef : null} style={{ width: '600px', height: '200px', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '48px', backgroundColor: idx % 2 === 0 ? '#f0f0f0' : '#e0e0e0', }} > {item} </div> ))} </div> {/* 切换按钮 */} <button onClick={handlePrev} style={{ position: 'absolute', left: '10px', top: '50%', transform: 'translateY(-50%)', padding: '8px 16px', cursor: 'pointer' }} > ← </button> <button onClick={handleNext} style={{ position: 'absolute', right: '10px', top: '50%', transform: 'translateY(-50%)', padding: '8px 16px', cursor: 'pointer' }} > → </button> </div> ); }; // 使用示例 const App = () => { const items = [1, 2, 3, 4]; return ( <div style={{ padding: '50px 0' }}> <InfiniteCarousel items={items} /> </div> ); }; export default App;
效果说明
- 向右滚动到4后,会平滑过渡到1,无任何回滚跳变;向左滚动到1后,平滑过渡到4
- 所有动画通过原生CSS
transition实现,没有依赖任何第三方库
效果示例图

内容的提问来源于stack exchange,提问作者Aeroxer Support
相关产品推荐
相关产品推荐

