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

윤코코·2021년 12월 22일
0

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)]

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

profile
Web Front-End Developer

0개의 댓글