<div id="root"></div>
$darkgray: #333;
$ivory: #fffef2;
$beige: #ebeade;
.CarouselItem {
flex-shrink: 0;
width: calc(100% / 3);
text-align: center;
font-size: 14px;
.itemImg {
display: flex;
align-items: flex-end;
height: 300px;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.textWrapper {
padding: 0 20px;
line-height: 1.33;
letter-spacing: -0.2px;
h5 {
margin: 10px 0;
line-height: 1.7;
}
}
}
.Carousel {
position: relative;
margin-top: 10px;
overflow: hidden;
button.navigation {
position: absolute;
top: calc(50% - 80px);
width: 80px;
height: 80px;
background-color: $darkgray;
color: $ivory;
text-align: center;
font-size: 16px;
z-index: 1;
transition: transform cubic-bezier(0.22, 0.61, 0.36, 1) 0.5s;
&.prev {
left: 0;
transform: translateX(-80px);
}
&.next {
right: 0;
transform: translateX(80px);
}
}
&:hover {
button.navigation {
transform: translateX(0);
}
}
.itemWrapper {
display: flex;
margin: 0 80px 48px;
transition: transform ease 0.5s;
}
.pagination {
height: 2px;
margin: 0 80px 20px;
background-color: rgba(0, 0, 0, 0.2);
.current {
height: 100%;
background-color: $darkgray;
transition: transform cubic-bezier(0.22, 0.61, 0.36, 1) 0.5s;
}
}
}
View Compiled
const { useState } = React;
const CarouselItem = ({ heading, description, alt, src }) => {
return (
<div className="CarouselItem">
<div className="itemImg">
<img alt={alt} src={src} />
</div>
<div className="textWrapper">
<h5>{heading}</h5>
<div className="description">{description}</div>
</div>
</div>
);
};
const Carousel = ({ className, dataList }) => {
const [slideIndex, setSlideIndex] = useState(0);
const onClick = event => {
const isPrev = event.target.className.includes('prev');
const isNext = event.target.className.includes('next');
if (isPrev) {
setSlideIndex(curr => --curr);
}
if (isNext) {
setSlideIndex(curr => ++curr);
}
};
const firstSlide =
slideIndex === 0
? {
style: { transform: 'translateX(-80px)' },
}
: null;
const lastSlide =
slideIndex === dataList.length - 3
? {
style: { transform: 'translateX(80px)' },
}
: null;
return (
<div className={`Carousel ${className}`}>
<button
className="navigation prev"
onClick={onClick}
style={firstSlide && firstSlide.style}
disabled={firstSlide}
>
<i className="fas fa-chevron-left prev" />
</button>
<div
className="itemWrapper"
style={{ transform: `translateX(calc(-100% / 3 * ${slideIndex}))` }}
>
{dataList &&
dataList.map(data => (
<CarouselItem
key={data.id}
heading={data.name}
description={data.description}
alt={data.name}
src={data.src}
/>
))}
</div>
<button
className="navigation next"
onClick={onClick}
style={lastSlide && lastSlide.style}
disabled={lastSlide}
>
<i className="fas fa-chevron-right next" />
</button>
<div className="pagination">
<div
className="current"
style={{
width: `calc(100% / ${dataList.length - 2})`,
transform: `translateX(calc(100% * ${slideIndex}))`,
}}
/>
</div>
</div>
);
};
const DATA = [
{
id: 0,
name: '요거트',
description: '첫 번째 설명',
src: 'https://images.unsplash.com/photo-1643121888146-9839fa08c8a2?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=387&q=80',
},
{
id: 1,
name: '빵',
description: '두 번째 설명',
src: 'https://images.unsplash.com/photo-1643114917776-78071774f6b8?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=387&q=80',
},
{
id: 2,
name: '샐러드',
description: '세 번째 설명',
src: 'https://images.unsplash.com/photo-1643127045144-7fe95e9888a2?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=387&q=80',
},
{
id: 3,
name: '과일',
description: '네 번째 설명',
src: 'https://images.unsplash.com/photo-1643131514219-1e480cce39aa?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=387&q=80',
},
{
id: 4,
name: '케이크',
description: '다섯 번째 설명',
src: 'https://images.unsplash.com/photo-1609050156152-5d064be292bb?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=387&q=80',
},
];
ReactDOM.render(<Carousel dataList={DATA} />, document.getElementById('root'));
View Compiled
This Pen doesn't use any external CSS resources.