App.jsx
import React from 'react';
import Carousel from './components/Carousel';
export default function App() {
return (
<>
<Carousel>
<img src='http://pic.bizhi360.com/bbpic/61/10961.jpg' alt=""/>
<img src='http://pic.bizhi360.com/bbpic/34/11234.jpg' alt=""/>
<img src='http://pic.bizhi360.com/bbpic/79/11079.jpg' alt=""/>
<img src='http://pic.bizhi360.com/bbpic/30/11230.jpg' alt=""/>
</Carousel>
</>
)
}
components / Carousel / index.jsx
import React,{useState, useEffect} from "react";
import './index.css';
let lock, isRun; // 节流锁、是否自动轮播
export default function Carousel(props) {
let [index, setIndex] = useState(0); // 第几张图片
let [isTransition, setIsTransition] = useState(true); // 是否过渡
const imgArrNum = props.children.length; // 图片数量
const time = props.time ?? 500; // 过渡时间 (如果没给值,则用默认值)
const carouselTime = time*2.4; // 自动轮播等待时间
let run; // 自动轮播定时器
// 自动轮播
useEffect(()=>{
if (isRun) return;
run = setTimeout(()=>{
throttle(next);
}, carouselTime);
})
// 节流
const throttle = (fn)=>{
if (lock) return;
fn();
lock = setTimeout(() => lock=!lock, time);
}
// 停止轮播
const stopRun = ()=>{
isRun = true;
clearTimeout(run);
}
// 继续轮播
const rerun = ()=>{
isRun = false;
run = setTimeout(()=>{
throttle(next);
}, carouselTime);
}
// 上一张
const prev = ()=>{
setIndex(index-1);
if (index === 0) {
setIsTransition(false);
setIndex(imgArrNum);
}
setTimeout(()=>{
setIsTransition(true);
index === 0 && setIndex(imgArrNum-1);
});
}
// 下一张
const next = ()=>{
setIsTransition(true);
setIndex(index+1);
if (index === imgArrNum-1) {
setTimeout(()=>{
setIsTransition(false);
setIndex(0);
}, time);
}
}
// 小圆点点击
const dotClick = (i)=>{
setIsTransition(true);
setIndex(i);
}
return (
<div className='carousel' onMouseEnter={stopRun} onMouseLeave={rerun}>
<ul className='show' style={{
left: `${-100 * index}%`,
width: `${(imgArrNum+1) * 100}%`,
transition: isTransition ? `${time}ms` : 'none'
}}>
{ // 遍历图片
props.children.map((img, index)=>{
return <li key={index}>{img}</li>
})
}
<li>{props.children[0]}</li>
</ul>
<p className='btn prev' onClick={()=>throttle(prev)}>❮</p>
<p className='btn next' onClick={()=>throttle(next)}>❯</p>
<ul className='dots'>
{ // 遍历小圆点
props.children.map((img, i)=>{
if (index === imgArrNum) index = 0;
return <li onClick={()=>dotClick(i)} key={i} className={`dot${index === i ? ' active' : ''}`}></li>
})
}
</ul>
</div>
)
}
components / Carousel / index.css
*{margin: 0; padding: 0}
li{list-style-type: none}
.carousel{
position: relative;
margin: 40px auto;
width: 90vw;
overflow: hidden;
}
.carousel:hover .btn{
transition: .5s;
opacity: .7;
}
.show{
display: flex;
position: relative;
}
.show img{
max-width: 100%;
}
.btn{
position: absolute;
top: 50%;
transform: translateY(-50%);
padding: 12px;
font-size: 24px;
color: #fff;
background-color: #282c34;
opacity: 0;
cursor: pointer;
user-select: none;
transition: .3s;
}
.btn:hover{
opacity: 1!important;
}
.prev{
border-radius: 0 4px 4px 0;
}
.next{
right: 0;
border-radius: 4px 0 0 4px;
}
.dots{
display: flex;
gap: 12px;
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 4%;
}
.dot{
width: 10px;
height: 10px;
background-color: #000;
border-radius: 50%;
opacity: .5;
cursor: pointer;
transition: .3s;
}
.dot:hover{
opacity: .95;
background-color: #fff;
}
.active{
width: 50px;
height: 8px;
opacity: .95;
background-color: #fff;
border-radius: 16px;
}