https://github.com/joepasss/single-carousel
GitHub - joepasss/single-carousel: single child carousel in react
single child carousel in react. Contribute to joepasss/single-carousel development by creating an account on GitHub.
github.com
작업내용 정리
1. 캐러셀 내부에 들어갈 자식 요소들 정렬하기
2. 캐러셀 자식 또는 부모 컨테이너 이동하기
시작 전 세팅
- 리엑트 세팅
import './singleCarousel.scss';
export const SingleCarousel = () => {
const [current, setCurrent] = useState<number>(0);
const buttons = [];
for (let i = 0; i < 5; i++) {
buttons.push(
<button
className='nav-btn'
key={i}
onClick={() => setCurrent(i)}
id={current === i ? 'current--img' : ''}
/>
);
}
return (
<section id='carousel'>
{current !== 0 && (
<button className='arrow-left' onClick={() => setCurrent(current - 1)}>
<
</button>
)}
{current !== 4 && (
<button className='arrow-right' onClick={() => setCurrent(current + 1)}>
>
</button>
)}
<div className='image-container'>
<ul className='items'>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
</div>
<div className='carousel-nav'>{buttons}</div>
</section>
);
};
- sass (css) 세팅
@import '../../styles/globalVar.scss';
section#carousel {
position: relative;
padding: 1rem 2rem;
height: 60vh;
width: 50rem;
button.arrow-left,
button.arrow-right {
position: absolute;
top: 50%;
transform: translateY(-50%);
font-size: 3rem;
font-weight: 500;
border: none;
background-color: transparent;
cursor: pointer;
z-index: 2;
}
button.arrow-left {
left: 0;
}
button.arrow-right {
right: 0;
}
div.image-container {
height: 100%;
width: 100%;
position: relative;
overflow: hidden;
ul {
width: 100%;
height: 100%;
position: relative;
transition: transform 0.35s ease-in-out;
li {
position: absolute;
width: 100%;
height: 100%;
background-color: white;
font-size: 4rem;
display: flex;
align-items: center;
justify-content: center;
border-radius: 1rem;
}
}
}
div.carousel-nav {
position: absolute;
bottom: -2rem;
height: 5%;
width: 100%;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
button.nav-btn {
width: 1.5rem;
height: 1.5rem;
background-color: transparent;
border: 4px solid black;
border-radius: 50%;
cursor: pointer;
}
button#current--img {
background-color: black;
}
}
}
li (캐러셀 자식요소) 정렬하기
1. 부모의 width, height와 동일한 크기로 적용되어 있으므로 부모 또는 자식의 width를 받아와 준다
2. 받아온 width값을 이용해서 다시 정렬해 준다 (width 가 1000이라 가정하면 첫 번째 자식의 left: 0, 두 번쨰: 1000, 세 번째: 2000 ...)
컨테이너 (부모, ul)의 width 가져오기
1. useRef를 이용, list라는 이름의 변수를 만들어준 뒤 ul에 ref로 적용시켜준다
import { useRef, RefObject } from 'react'
export const SingleCarousel = () => {
const list = useRef() as RefObject<HTMLUListElement>;
// ... 생략 ...
return (
<section id='carousel'>
// ... 생략 ...
<div classname='image-container>
<ul className='items' ref={list}>
// ... 생략 ...
</ ul>
</div>
</section>
);
};
2. list.current 에 들어있는 children중 하나의 width를 가져오기 위해 useEffect 를 사용, useEffect 내에서 const 를 지정한 뒤 저장해 놓는다
useEffect(() => {
const imgWidth = list.current!.children[0].getBoundingClientRect().width + 20;
});
이미지 사이에 간격을 주기 위해 20을 더했다
3. 이미지의 left값을 결정해준다 ul의 각 자식 (list.current!.children[]) 모두에 left값을 지정해 준다 이 때 left값은 이전에 구했던 imgWidth * index + 'px' 이 된다 (첫 번째 자식 0, 두 번째 자식 1000, 세 번쨰 자식 2000 ....)
- useEffect 내부에서 함수를 만든 다음 이미지 array를 만든 다음 foreach로 순회하며 적용시킨다
useEffect(() => {
const imgWidth =
list.current!.children[0].getBoundingClientRect().width + 20;
const setImgPosition = (img: HTMLElement, index: number) => {
img.style.left = imgWidth * index + 'px';
};
const imgs = Array.from(list.current!.children) as HTMLElement[];
imgs.forEach(setImgPosition);
});
이미지 정렬된 모습
4. 스크롤 하듯이 이동을 하기 위해 ul(container) 에 transform 옵션을 준다
useEffect(() => {
const imgWidth =
list.current!.children[0].getBoundingClientRect().width + 20;
const setImgPosition = (img: HTMLElement, index: number) => {
img.style.left = imgWidth * index + 'px';
};
const imgs = Array.from(list.current!.children) as HTMLElement[];
imgs.forEach(setImgPosition);
list.current!.style.transform =
'translateX(-' + imgs[current].style.left + ')';
});