React无依赖实现环形无限轮播:仿Aniwave站点轮播功能问询
实现无缝无限轮播的解决方案
要实现目标站点那样的无缝无限轮播,将第一张幻灯片追加到末尾、最后一张放到开头的首尾克隆方案是最简洁且实用的选择——直接调整偏移量会出现明显跳变,无法做到视觉上的“无限”效果,而克隆幻灯片+过渡后瞬间重置位置的逻辑,能完美模拟无限滚动的体验。
以下是修改后的完整代码:
import { useRef, useEffect, useState } from "react"; import { details } from "./constants"; const App = () => { const carouselTrackRef = useRef(null); const containerRef = useRef(null); const [counter, setCounter] = useState(1); // 初始指向原第一张(因为前面多了克隆的最后一张) const slideWidthRef = useRef(0); // 获取幻灯片宽度并监听窗口变化 useEffect(() => { const updateSlideWidth = () => { const firstSlide = carouselTrackRef.current?.children[0]; if (firstSlide) { slideWidthRef.current = firstSlide.offsetWidth; carouselTrackRef.current.style.left = `-${slideWidthRef.current * counter}px`; } }; updateSlideWidth(); window.addEventListener("resize", updateSlideWidth); return () => window.removeEventListener("resize", updateSlideWidth); }, [counter]); const updateTrackPosition = () => { carouselTrackRef.current.style.left = `-${slideWidthRef.current * counter}px`; }; const prevSlide = () => { setCounter(prev => prev - 1); updateTrackPosition(); }; const nextSlide = () => { setCounter(prev => prev + 1); updateTrackPosition(); }; // 过渡结束后处理无缝重置逻辑 useEffect(() => { const handleTransitionEnd = () => { const slideCount = details.length; // 滚动到克隆的第一张(原最后一张),瞬间跳转到原最后一张 if (counter === 0) { setCounter(slideCount); carouselTrackRef.current.style.transition = "none"; updateTrackPosition(); // 重新开启过渡动画 setTimeout(() => { carouselTrackRef.current.style.transition = "all 0.5s ease"; }, 10); } // 滚动到克隆的最后一张(原第一张),瞬间跳转到原第一张 else if (counter === slideCount + 1) { setCounter(1); carouselTrackRef.current.style.transition = "none"; updateTrackPosition(); setTimeout(() => { carouselTrackRef.current.style.transition = "all 0.5s ease"; }, 10); } }; const track = carouselTrackRef.current; track?.addEventListener("transitionend", handleTransitionEnd); return () => track?.removeEventListener("transitionend", handleTransitionEnd); }, [counter]); // 键盘导航事件 useEffect(() => { const handleKeyDown = (e) => { if (e.code === "ArrowRight") nextSlide(); if (e.code === "ArrowLeft") prevSlide(); }; window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); }, []); return ( <div className="w-screen h-screen flex items-center bg-slate-500"> <div ref={containerRef} className="w-screen h-2/3 overflow-x-hidden relative border-cyan-800 border-4" > <div onClick={prevSlide} className="absolute cursor-pointer text-2xl text-white z-10 left-0 top-1/2" > Prev </div> <div onClick={nextSlide} className="absolute cursor-pointer text-2xl text-white z-10 right-0 top-1/2" > Next </div> <div ref={carouselTrackRef} className="h-full flex transition-all duration-500 ease-in-out absolute bg-red-800" > {/* 克隆最后一张幻灯片放到最前面 */} <div className="w-screen h-full flex-shrink-0" key={`clone-last-${details[details.length - 1].title}`} > <div className="rounded-lg h-full"> <img src={details[details.length - 1].image} alt="clone-last" className="rounded-lg w-full h-full object-cover" /> </div> </div> {/* 原幻灯片列表 */} {details.map((slide) => ( <div className="w-screen h-full flex-shrink-0" key={slide.title} > <div className="rounded-lg h-full"> <img src={slide.image} alt={slide.title} className="rounded-lg w-full h-full object-cover" /> </div> </div> ))} {/* 克隆第一张幻灯片放到最后面 */} <div className="w-screen h-full flex-shrink-0" key={`clone-first-${details[0].title}`} > <div className="rounded-lg h-full"> <img src={details[0].image} alt="clone-first" className="rounded-lg w-full h-full object-cover" /> </div> </div> </div> </div> </div> ); }; export default App;
核心逻辑说明:
- 首尾克隆:在原幻灯片列表前后分别添加最后一张和第一张的克隆项,让用户滚动到边界时看到的是衔接的内容,视觉上形成无限循环的错觉。
- 过渡后重置:监听轮播轨道的
transitionend事件,当滚动到克隆项时,瞬间取消过渡动画、重置偏移量到真实的首尾幻灯片位置,再重新开启过渡——这个过程极快,用户完全感知不到跳变。 - 响应式适配:监听窗口大小变化,实时更新幻灯片宽度并调整轮播位置,保证在不同屏幕尺寸下都能正常工作。
- 内存泄漏防护:所有全局事件监听都在组件卸载时移除,避免不必要的资源占用。
内容的提问来源于stack exchange,提问作者Akif Ahmed
相关产品推荐
相关产品推荐

