1. 구현방법
React에는 여러 사진 슬라이드 기능을 제공하는 라이브러리들이 있지만, 사진 슬라이드의 경우 해당 라이브러리의 사용법을 알아야 하는 번거로움 보다 직접 구현하는 것이 훨씬 쉬운것 같아 직접 구현하는 내용을 포스팅 해보려고 한다.
사진 슬라이드 기능 구현은 여러 장의 사진이 x축 방향으로 한 장씩 넘어가는 기능을 말한다.
데이터는 아래와 같이 이디야 신메뉴 4장으로 구성되어 있다.
export const screenImgs = {
maxIndex: 4,
imgs: [
'https://buza.biz/data/editor/2212/thumb-fba1c023d4b13d9e80fe0c4b2335d8ee_1670463786_1854_600x750.jpg',
'https://www.shinailbo.co.kr/news/photo/202204/1539283_721165_253.jpg',
'https://dimg.donga.com/wps/NEWS/IMAGE/2020/05/06/100920864.2.jpg',
'https://www.kmaeil.com/news/photo/202110/310012_139668_925.jpg',
],
};
필자는 ts환경에서 styled-components를 사용하고 있습니다.
사진 슬라이드 구현
1. 이미지 데이터 불러오기와 현재 보고 있는 이미지 index설정
const { maxIndex, imgs } = screenImgs;
const [imgIndex, setImgIndex] = useState(0);
현재 보고 있는 페이지의 index를 알아야하기 때문에, imgIndex State
를 선언해준다.
그리고 이미지 불러옴과 동시에 이미지의 개수를 알아야 다시 처음으로 돌아갈 수 있기 때문에 maxIndex를 받아왔다.
2. 5초마다 슬라이드 변경
useEffect(() => {
const timer = setInterval(() => {
setImgIndex((prev) => (prev === maxIndex - 1 ? 0 : prev + 1));
}, 5000);
return () => clearInterval(timer);
}, [maxIndex]);
imgIndex
에 따라 보여지는 이미지가 변경되기 때문에, 맨 끝 페이지가 아니라면, 5초마다 imgIndex+1
을 해준다.
그리고 맨 끝 이미지에 도착했다면, prev === maxIndex - 1 ? 0
조건문과 같이 첫번째 이미지로 돌아가도록 설정해주었다.
3. CSS 트랜스폼을 활용한 슬라이드 효과
핵심 로직은 SliderContainer
라는 이미지들을 가로로 붙인 컨테이너를 imgIndex에 따라 위치를 변화시키는 것이다. 위치변화에 따라 사용자가 보이는 화면은 사진이 슬라이더로 넘어가는 것 처럼 보인다.
<SliderContainer $translateX={-imgIndex * 100}>
{imgs.map((img, index) => (
<Slide key={index}>
<SlideImage src={img} alt={`slide-${index}`} />
</Slide>
))}
</SliderContainer>
SliderContainer
같은 컴포넌트에서img.map
을 통해 배열을 순회하여 각 SlideImage
를 가로로 붙인다.$translateX={-imgIndex * 100}
에서 100은 width 100%를 의미한다.imgIndex State
는 지속적으로 바뀌기 때문에 결국 SliderContainer
에게 5초마다 변경되는 $translateX
를 전달해주게 된다.
const SliderContainer = styled.div<{
$translateX: number;
}>`
display: flex;
width: 100%;
height: 100%;
transform: translateX(${({ $translateX }) => $translateX}%);
transition: transform 0.5s ease-in-out;
position: absolute;
top: 0;
left: 0;
`;
핵심적인 부분은 CSS코드이다.
기본적으로 display:flex
를 통해 가로로 이미지들을 배치한 것을 확인할 수 있고,transform
으로 $translateX
를 인자로 받아, 사용자에게 보이는 X좌표를 5초마다 변경한다.transition
로 transform 애니메이션을 부드럽게 만들어 사진이 자연스럽게 넘어가는 효과를 구현할 수 있다.
4. Indicators 구현
<Indicators>
{Array.from({ length: maxIndex }).map((_, index) => (
<Indicator
key={index}
$isActive={index === imgIndex}
onClick={(e) => {
e.stopPropagation();
setImgIndex(index);
}}
/>
))}
</Indicators>
사진 슬라이드와 동일하게 O를 표시해주는 Indicator
를 배열로 좌우로 image개수만큼 붙여준다.
1번째 사진인 경우 1번째 Indicator
의 $isActive
를 전달해 줌으로 흰색으로 표현할 수 있다.
또한 onClick이벤트를 통해 클릭시 해당 이미지 순서로 넘어갈 수 있도록 구현할 수도 있다.
2. 결과 화면
결과 화면
전체 코드
import { useEffect, useState } from 'react';
import styled from 'styled-components';
import { screenImgs } from '../../../data/screen';
const LockPage = () => {
const { maxIndex, imgs } = screenImgs;
const [imgIndex, setImgIndex] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setImgIndex((prev) => (prev === maxIndex - 1 ? 0 : prev + 1));
}, 5000);
return () => clearInterval(timer);
}, [maxIndex]);
return (
<BaseContainer>
<SliderWrapper>
<SliderContainer $translateX={-imgIndex * 100}>
{imgs.map((img, index) => (
<Slide key={index}>
<SlideImage src={img} alt={`slide-${index}`} />
</Slide>
))}
</SliderContainer>
<Indicators>
{Array.from({ length: maxIndex }).map((_, index) => (
<Indicator
key={index}
$isActive={index === imgIndex}
onClick={(e) => {
e.stopPropagation();
setImgIndex(index);
}}
/>
))}
</Indicators>
</SliderWrapper>
</BaseContainer>
);
};
export default LockPage;
const BaseContainer = styled.div`
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
`;
const SliderWrapper = styled.div`
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
`;
const SliderContainer = styled.div<{
$translateX: number;
}>`
display: flex;
width: 100%;
height: 100%;
transform: translateX(${({ $translateX }) => $translateX}%);
transition: transform 0.5s ease-in-out;
position: absolute;
top: 0;
left: 0;
`;
const Slide = styled.div`
min-width: 100%;
width: 100%;
height: 100%;
flex-shrink: 0;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
position: relative;
`;
const SlideImage = styled.img`
width: 100%;
height: 100%;
object-fit: fill;
object-position: center;
`;
const Indicators = styled.div`
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 10px;
z-index: 10;
`;
const Indicator = styled.div<{ $isActive: boolean }>`
width: 10px;
height: 10px;
border-radius: 50%;
background-color: ${({ $isActive }) =>
$isActive ? '#fff' : 'rgba(255, 255, 255, 0.5)'};
cursor: pointer;
transition: background-color 0.3s ease;
`;
'React' 카테고리의 다른 글
[React] BottomSheet 드래그 이벤트 스로틀링 최적화 (requestAnimationFrame) (1) | 2025.06.08 |
---|---|
[Vite] vite.config.js 설정 (proxy, console제거) (0) | 2025.05.26 |
[React] State(상태) 끌어올리기로 리렌더링 문제 해결하기 (0) | 2025.04.24 |
[React] GlobalStyle 폰트 적용 안됨, 폰트 깜빡임 현상 원인 및 해결방법 (0) | 2025.04.14 |
[React] 페이지 있는 게시판 리스트 만들기 (styled-components) (2) | 2024.12.15 |