시연영상

Carousel 컴포넌트
import { useEffect, useState } from "react";
export default function Carousel() {
const [activeIndex, setActiveIndex] = useState(0);
const imgArr = [
{
url: "https://images.unsplash.com/photo-1706539214505-5cb21db70d12?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcwNzY1OTg1Mw&ixlib=rb-4.0.3&q=80&w=1080",
id: 0,
},
{
url: "https://images.unsplash.com/photo-1464822759023-fed622ff2c3b?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1470&q=80",
id: 1,
},
{
url: "https://plus.unsplash.com/premium_photo-1667340456421-e39b77a25217?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcwNzY1OTkwMg&ixlib=rb-4.0.3&q=80&w=1080",
id: 2,
},
{
url: "https://images.unsplash.com/photo-1606117331085-5760e3b58520?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1470&q=80",
id: 3,
},
{
url: "https://images.unsplash.com/photo-1704975986930-0c09f513c985?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcwNzY2MDY5Mw&ixlib=rb-4.0.3&q=80&w=1080",
id: 4,
},
];
const clickNext = (ev: React.MouseEvent<HTMLSpanElement, MouseEvent>) => {
ev.stopPropagation();
if (activeIndex === imgArr.length - 1) {
setActiveIndex(0);
} else {
setActiveIndex(activeIndex + 1);
}
};
const clickPrev = (ev: React.MouseEvent<HTMLSpanElement, MouseEvent>) => {
ev.stopPropagation();
if (activeIndex === 0) {
setActiveIndex(imgArr.length - 1);
} else {
setActiveIndex(activeIndex - 1);
}
};
const clickDot = (
ev: React.MouseEvent<HTMLLabelElement, MouseEvent>,
id: number
) => {
ev.stopPropagation();
setActiveIndex(id);
};
const indexClassName = (id: number) => {
return id === activeIndex
? "carousel__img-box-active"
: "carousel__img-box";
};
useEffect(() => {
const interval = setInterval(() => {
setActiveIndex(() =>
activeIndex === imgArr.length - 1 ? 0 : activeIndex + 1
);
}, 5000);
return () => clearInterval(interval);
}, [activeIndex]);
return (
<>
<div className="carousel">
<div className="carousel__controls">
<span
className="carousel__slide-prev"
onClick={(ev) => clickPrev(ev)}
>
‹
</span>
<span
className="carousel__slide-next"
onClick={(ev) => clickNext(ev)}
>
›
</span>
</div>
<div className="carousel__img">
{imgArr.map((img) => (
<div className={indexClassName(img.id)} key={img.id}>
<img src={img.url} alt={`img-${img.id}`} />
</div>
))}
</div>
<div className="carousel__dots">
{imgArr.map((el) => (
<label
key={el.id}
onClick={(ev) => clickDot(ev, el.id)}
className={
activeIndex === el.id ? "carousel__dot-active" : "carousel__dot"
}
></label>
))}
</div>
</div>
</>
);
}
css
.carousel {
width: 100%;
height: 100%;
position: relative;
}
.carousel:hover .carousel__controls {
opacity: 1;
}
.carousel__controls {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 100px;
line-height: 400px;
color: #fff;
padding: 0 10px;
box-sizing: border-box;
opacity: 0;
transition: all 0.7s ease-in-out;
}
.carousel__controls span {
cursor: pointer;
opacity: 0.5;
}
.carousel__controls span:hover {
opacity: 1;
}
.carousel__img {
width: 100%;
height: 100%;
display: flex;
}
.carousel__img-box-active {
max-width: 680px;
width: 100%;
height: 100%;
display: flex;
transition: opacity 0.8s ease-in-out;
}
.carousel__img-box {
opacity: 0;
flex: 1;
}
.carousel__img-box img,
.carousel__img-box-active img {
width: 100%;
height: 100%;
}
.carousel__dots {
width: 100%;
height: 40px;
position: absolute;
bottom: 15px;
display: flex;
gap: 7px;
align-items: center;
justify-content: center;
opacity: 0.8;
}
.carousel__dot-active {
width: 13px;
height: 13px;
background-color: #a4a1a1;
border-radius: 50%;
transition: background 0.5s ease-in-out;
}
.carousel__dot {
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #fff;
}