react实现无缝轮播组件
1.组件定义
import React, { useState, useEffect, useCallback } from "react";
import "./style.component.css";interface SeamlessCarouselProps {imageList: string[];autoPlaying?: boolean;
}const SeamlessCarousel: React.FC<SeamlessCarouselProps> = ({imageList,autoPlaying = true,
}) => {const [currentIndex, setCurrentIndex] = useState(1); const [isTransitioning, setIsTransitioning] = useState(true);const [isAutoPlaying, setIsAutoPlaying] = useState(autoPlaying);const [isPaused, setIsPaused] = useState(false);const extendedImages = [imageList[imageList.length - 1],...imageList,imageList[0],];const nextSlide = useCallback(() => {setCurrentIndex((prev) => {if (prev >= extendedImages.length - 1) {return prev;}return prev + 1;});setIsTransitioning(true);}, []);const prevSlide = () => {setCurrentIndex((prev) => {if (prev <= 0) {return prev;}return prev - 1;});setIsTransitioning(true);};useEffect(() => {if (!isAutoPlaying || isPaused) return;if (!isAutoPlaying) return;const interval = setInterval(nextSlide, 3000);return () => clearInterval(interval);}, [isAutoPlaying, isPaused, nextSlide]);useEffect(() => {if (currentIndex === extendedImages.length - 1) {const timeout = setTimeout(() => {setIsTransitioning(false);setCurrentIndex(1);}, 500);return () => clearTimeout(timeout);}if (currentIndex === 0) {const timeout = setTimeout(() => {setIsTransitioning(false);setCurrentIndex(imageList.length);}, 500);return () => clearTimeout(timeout);}setIsTransitioning(true);}, [currentIndex, extendedImages.length, imageList.length]);return (<divclassName={`carousel-container ${isPaused ? "show-arrows" : ""}`}onMouseEnter={() => setIsPaused(true)}onMouseLeave={() => setIsPaused(false)}><divclassName="carousel-list"style={{transform: `translateX(-${currentIndex * 100}%)`,transition: isTransitioning? "transform 0.5s ease": "none",}}>{extendedImages.map((image, index) => (<divkey={index}className="carousel-item"style={{ backgroundImage: `url(${image})` }}></div>))}</div><divclassName="carousel-arrow carousel-arrow-left"onClick={prevSlide}><span className="arrow-left"></span></div><divclassName="carousel-arrow carousel-arrow-right"onClick={nextSlide}><span className="arrow-right"></span></div><div className="circles-container">{imageList.map((_, index) => (<spankey={index}className={`circle ${index === currentIndex - 1 ? "active" : ""}`}onClick={() => setCurrentIndex(index + 1)}></span>))}</div><div className="autoplay-control"><button onClick={() => setIsAutoPlaying(!isAutoPlaying)}>{isAutoPlaying ? "暂停" : "播放"}</button></div></div>);
};export default SeamlessCarousel;
2.样式设置
.carousel-container {display: flex;position: relative;align-items: center;justify-content: center;scroll-behavior: smooth;outline: 1px solid #dddedc;border-radius: 10px;width: 900px;height: 600px;margin: 30px auto;overflow: hidden;
}.carousel-list {display: flex;position: relative;height: 100%;width: 100%;scroll-snap-align: start;aspect-ratio: 5 / 3;
}.carousel-item {flex: 0 0 100%;height: 100%;background-size: cover;background-repeat: no-repeat;background-position: center;
}.carousel-arrow {position: absolute;width: 40px;height: 40px;border-radius: 50%;background: rgba(255, 255, 255, 0.7);display: flex;align-items: center;justify-content: center;cursor: pointer;z-index: 10;transition: opacity 0.3s ease, background 0.3s ease;opacity: 0;
}.carousel-container.show-arrows .carousel-arrow {opacity: 1; /* 悬停时显示 */
}.carousel-arrow:hover {background: rgba(255, 255, 255, 0.9);
}.carousel-arrow-left {left: 10px;
}.carousel-arrow-right {right: 10px;
}.arrow-left,
.arrow-right {width: 12px;height: 12px;border-top: 2px solid #333;border-right: 2px solid #333;
}.arrow-left {transform: rotate(-135deg);margin-left: 4px;
}.arrow-right {transform: rotate(45deg);margin-right: 4px;
}/* 圆点指示器 */
.circles-container {display: flex;position: absolute;bottom: 20px;left: 50%;transform: translateX(-50%);gap: 20px;
}.circle {display: block;width: 24px;height: 24px;border-radius: 50%;background: transparent;border:1px solid rgba(255, 255, 255, 0.7);cursor: pointer;transition: all 0.3s ease;
}.circle.active {background-color: rgb(249, 246, 246);border-color: white;
}.circle:hover {background-color: rgba(255, 255, 255, 0.5);
}.autoplay-control {position: absolute;top: 15px;right: 15px;
}.autoplay-control button {background: rgba(0, 0, 0, 0.2);color: white;border: none;padding: 5px 10px;border-radius: 4px;cursor: pointer;
}.autoplay-control button:hover {background: rgba(0, 0, 0, 0.7);
}
3.组件使用
import SeamlessCarousel from "@/components/carousel/SeamlessMo"import one from "@/components/carousel/img/abstract-2512412.jpg";
import two from "@/components/carousel/img/ai-generated-8061340.jpg";
import three from "@/components/carousel/img/asian-422700.jpg";
import four from "@/components/carousel/img/binary-978942.jpg";
import five from "@/components/carousel/img/code-113611.jpg";
import six from "@/components/carousel/img/fruit-7048114.jpg";
import seven from "@/components/carousel/img/moss-4930309.jpg";
import eight from "@/components/carousel/img/wood-591631.jpg";const sampleImages = [one, two, three, four, five, six, seven, eight];const CarouselPage: React.FC = () => {return (<><h1>无缝轮播页面</h1><div className="mb-4"><SeamlessCarousel imageList={sampleImages} autoPlaying={false} /></div></>)
}export default CarouselPage;
4.使用测试
需要用到的图片请自行放到对应的目录,再将图片换成实际的路径即可。