캐러셀(Carousel) 만들기 (3)

Salt·2024년 3월 21일
0

Carousel

목록 보기
3/4

무한 캐러셀이 어떻게 가능할까 고민하다 답이 안나와서 검색 해봤다. 첫번째 이미지를 복사해서 이미지 마지막 부분에 추가하고, 마지막 이미지를 복사해서 이미지 첫 부분에 추가하면 된다고 한다.

이렇게 하면 이미지 마지막에 도달하면 마지막 이미지에서 첫번째 이미지로 자연스럽게 돌아간 것 처럼 보이는데 그럼 두번째 이미지는 어떻게 할까? 지금은 모르겠지만 아마도 마지막 이미지로 간다면 그 이미지에서 잠깐 멈춰있고, 멈춰있는 잠깐의 시간동안 translateX(0%)으로 이동하는 것 같다. 하지만 이렇게 한다면 이런 문제가 생길것 같다.

  1. 내 캐러셀은 3초마다 다음 이미지로 이동하는데 이렇게 한다면 첫번째 이미지에서 두번째 이미지로 이동할때 약간의 딜레이가 발생하지 않을까? 왜냐하면 마지막 이미지에서 첫번째 이미지로 이동하는 시간이 3초에 더해지는거니까.
  2. 지금은 괜찮지만 이동 버튼을 추가한 뒤 빠르게 버튼을 클릭한다면 마지막 이미지에서 첫번째 이미지로 이동하는 transition 시간 때문에 문제가 생길 것 같다.

예상되는 문제는 이정도고 어떤 방법으로 구현하는진 알았으니 혼자 생각해 보면서 구현해 봐야겠다.

    <SliderContainer>
      <Slider $total={total} $index={index}>
        <SlideImage src={makeBgPath(movies[total - 1].backdrop_path)} />
        {movies?.map((movie) => (
          <SlideImage key={movie.id} src={makeBgPath(movie.backdrop_path)} />
        ))}
        <SlideImage src={makeBgPath(movies[0].backdrop_path)} />
      </Slider>
    </SliderContainer>

이미지를 추가 해보고 캐러셀이 돌아가는걸 보면... 뭔가 순서가 잘못됐다. 듄은 마지막 이미지로 나와야 하는데 첫번째 이미지로 나오고있다. (첫번째 이미지는 쿵푸팬더, 마지막은 듄)

당연하게도 단순히 이미지만 앞, 뒤에 추가했기 때문에 제대로 작동하지 않는다. 이미지가 늘어났으니 index state하고 useEffect안에 조건문을 수정하면 다음과 같이 작동한다.

하지만 캐러셀 마지막에서 앞으로 이동하는 과정이 다 보이는건 똑같은데 이건 transition 때문에 그렇다. 마지막 이미지에 도달하면 transition 효과를 없애면 될듯.

const [carouselTransition, setCarouselTransition] = useState(
    `transform 500ms ease-in-out`
  );

  useEffect(() => {
    if (total === undefined) return;
    const interval = setInterval(() => {
      setIndex((prev) => {
        if (prev === total + 1) {
          setCarouselTransition("");
          return 1;
        }
        setCarouselTransition(`transform 500ms ease-in-out`);
        return prev + 1;
      });
    }, 3000);

    return () => clearInterval(interval);
  }, [total]);
  
  const Slider = styled.ul<{
  $index: number;
  $transition: string;
}>`
  display: flex;
  transition: ${(props) => props.$transition};
  transform: translateX(${(props) => props.$index * -100}%);
`;
  1. slider의 styled-component는 carouselTransition을 받아 transition 효과를 준다.
  2. index가 이미지 최대개수(total+1)에 도달하면 carouselTransition을 ""로 초기화.

이것만으로 캐러셀 마지막에서 앞으로 이동할때 발생하는 문제를 해결할 수 있지만 이렇게만 하면 index가 초기화 됐을때 transition 효과가 없어서 옆으로 넘어가는 효과가 발생하지 않는다.

  1. index가 초기화 된다면 setCarouselTransition(transform 500ms ease-in-out)으로 다시 transition 효과를 준다.

이제 자연스럽게 마지막에서 첫번째 이미지로 돌아가지지만.. 첫번째 이미지에서 두번째 이미지로 넘어갈때 상당히 긴 딜레이가 발생하는데 내 예상보다 더 심각하다. 6초뒤에 두번째 이미지로 넘어가는걸 보면 마지막 이미지(첫번째 이미지를 복사한) 3초, 첫번째 이미지 3초 이렇게 6초인 것 같다.

이것도 setCarouselTransition을 초기화 했을때 처럼 마지막 이미지일때 인터벌을 멈추고 index가 초기화 된다면 다시 인터벌을 시작하면 될거라 생각했지만 더 간단한 방법이 떠올랐다.

  useEffect(() => {
    if (total === undefined) return;
    const interval = setInterval(() => {
      setIndex((prev) => {
        if (prev === total + 1) {
          setCarouselTransition("");
          setSeconds(0);
          return 1;
        }
        setCarouselTransition(`transform 500ms ease-in-out`);
        setSeconds(3000);
        return prev + 1;
      });
    }, seconds);

Interval의 delay를 second state로 설정하고 마지막 이미지일땐 0으로 그 외에는 3000으로 세팅하는거다. 이제 모든게 정상적으로 작동한다.

이제 해결할 문제는
1. 큰 화면에서 이미지가 제대로 안나오는 것
2. div>img에서 ul>li>img로 변경
3. 모든 이미지가 아니라 랜덤으로 이미지 5개만 선택해 보여주기

더 추가할건
1. 양 옆에 이동버튼 추가
2. 이미지 아래 버튼을 추가해 현재 몇번째 이미지인지 표시

0개의 댓글