Lazy Loading을 적용해서 이미지 리스트를 만들어두고 보니
클릭하면 이미지를 확대해서 보여주고 싶었다.
특히나 중년인 주유소 사장님들은 가능한 모든걸 크게크게 보여드려야겠다는 생각이 있다. (그러고 보니까 글씨가 전체적으로 조금 작은 것 같기도 하고...)
이미지를 클릭하면 크게 보이고 옆으로 넘길 수 있는 걸
Image Carousel, Slider 등의 이름으로 부르는 것 같다.
많이 쓰이는 라이브러리도 있는 것 같지만
직접 구현하는 사람들도 있어서
개념원리를 이해할겸 나도 직접 구현해보았다.
핵심은 이미지를 가로로 나열한 긴 박스를 overflow: hidden
로 둔 후에
currentSlide에 비례해서 translateX를 하는 것이다.
그리고 RefObject의 current에 style이 있다는 걸 처음 알았다.
const [currentSlide, setCurrentSlide] = useState<number>(clickedSlide);
const slideRef = useRef<HTMLDivElement>(null);
// currentSlide에 따라서 translateX 시키는 부분
useEffect(() => {
if (!slideRef || !slideRef.current) return;
slideRef.current.style.transition = "all 0.5s ease-in-out";
slideRef.current.style.transform = `translateX(-${currentSlide}00%)`;
}, [currentSlide]);
return (
<CancelImageCarouselBG>
// 모든 이미지가 가로로 나열되어있고, overflow: hidden 되어있는 부분
<ImageCarouselBox ref={slideRef}>
{cancelImgURL.map((url: string, index: number) => (
<div key={index}>
<div>
<img src={url} alt="Cancel Request Image" />
</div>
</div>
))}
</ImageCarouselBox>
// currentSlide를 더하고 빼는 부분
<CarouselBtnBox>
<button onClick={prevSlide}>
<img src={Bracket} alt="Bracket" />
<span>이전</span>
</button>
<span>
{currentSlide + 1}/{cancelImgURL.length}
</span>
<button onClick={nextSlide}>
<span>다음</span>
<img src={Bracket} alt="Bracket" />
</button>
</CarouselBtnBox>
</CancelImageCarouselBG>
)
👉 CancelImageCarousel.tsx 전체 보기
그런데 여기서 문제는 모든 이미지의 비율을 동일하게 고정하고 싶은데
이것 참 생소한 상황이었다..
구글링을 해보니
가장 상위에 있는 첫번째 wrapper에 비율의 기준이 되는 width를 지정하고
그 자식이 되는 두번째 wrapper는 padding-top: 100%
를 줘서
비율을 정사각형으로 고정할 수 있었다!!
그리고 그 위에 이미지를 넣을 div를 position: absolute
로 띄운다.
다만, 첫번째 wrapper의 width가 화면이 커짐에 따라 너무 커지면 사진이 화면을 꽉 채워버려서 max-width로 제한을 해줬다.
const ImageCarouselBox = styled.div`
width: 100%;
max-width: 65rem;
display: flex;
> div {
background-color: white;
padding-top: 100%;
min-width: 100%;
position: relative;
overflow: hidden;
box-shadow: 0px 4px 50px rgba(0, 0, 0, 0.25);
> div {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
-webkit-transform: translate(50%, 50%);
-ms-transform: translate(50%, 50%);
transform: translate(50%, 50%);
> img {
position: absolute;
top: 0;
left: 0;
max-width: 100%;
height: auto;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
}
}
`;
이건 생각보다 아주 간단하게 해결되었다!
각 이미지 요소를 map을 통해 렌더링할때에 index를 props로 넣어둔 후
해당 이미지를 클릭해서 Carousel을 열때에 그 index를 캐러셀에서 넘겨받아 currentSlide의 initial state로 사용하도록 하는 것이다.
const CancelImageCarousel = ({
setIsImageCarouselOpen,
clickedSlide,
}: CancelImageCarouselProps) => {
const location = useLocation();
const { cancelImgURL } = location.state;
const TOTAL_SLIDES = cancelImgURL.length - 1;
const [currentSlide, setCurrentSlide] = useState<number>(clickedSlide);
(생략)
👉 CancelImageCarousel.tsx 전체 보기
👉 CustomerCancelRequest.tsx 보기 (이미지 리스트가 있는 부분)
이렇게 완성!
뿌듯-
peppermint100님의 [React Hooks로 Carousel Slider 만들기]
흉내쟁이님의 [CSS로 유동적인 컨테이너 너비에 기반한 이미지 비율 유지, 가운데 정렬, 자르기]
서상혁님의 [리액트 슬라이드 ⏩ / 캐로셀 (Carousel)]
왠지모르게 이미지 타이쿤이라고 부르고 싶은...