||Project1|| #10 Image Carousel 만들기

Lazy Loading을 적용해서 이미지 리스트를 만들어두고 보니
클릭하면 이미지를 확대해서 보여주고 싶었다.

특히나 중년인 주유소 사장님들은 가능한 모든걸 크게크게 보여드려야겠다는 생각이 있다. (그러고 보니까 글씨가 전체적으로 조금 작은 것 같기도 하고...)

이미지를 클릭하면 크게 보이고 옆으로 넘길 수 있는 걸
Image Carousel, Slider 등의 이름으로 부르는 것 같다.

많이 쓰이는 라이브러리도 있는 것 같지만
직접 구현하는 사람들도 있어서
개념원리를 이해할겸 나도 직접 구현해보았다.

🎠 Image Carousel 만들기

핵심은 이미지를 가로로 나열한 긴 박스를 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 보기 (이미지 리스트가 있는 부분)

이렇게 완성!
뿌듯-

🧐 해결되지 않는 궁금증

  • 이미지 간의 간격을 띄우고 싶은데 margin을 넣어주면 translateX를 통해 다음 이미지로 넘길때 이미지 이동거리에 오차가 생겨서 조금만 넘겨도 이미지가 정중앙에서 벗어나 버린다. 이 부분은 분명 조금 더 생각을 해보자...

|| 참고 ||

peppermint100님의 [React Hooks로 Carousel Slider 만들기]
흉내쟁이님의 [CSS로 유동적인 컨테이너 너비에 기반한 이미지 비율 유지, 가운데 정렬, 자르기]
서상혁님의 [리액트 슬라이드 ⏩ / 캐로셀 (Carousel)]

왠지모르게 이미지 타이쿤이라고 부르고 싶은...

좋은 웹페이지 즐겨찾기