캐러셀 구현

dooga·2024년 2월 14일
0

기능

목록 보기
1/2
post-thumbnail

시연영상

import { useEffect, useState } from "react";

export default function Carousel() {
  const [activeIndex, setActiveIndex] = useState(0);

  const imgArr = [
    {
      url: "https://images.unsplash.com/photo-1706539214505-5cb21db70d12?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcwNzY1OTg1Mw&ixlib=rb-4.0.3&q=80&w=1080",
      id: 0,
    },
    {
      url: "https://images.unsplash.com/photo-1464822759023-fed622ff2c3b?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1470&q=80",
      id: 1,
    },
    {
      url: "https://plus.unsplash.com/premium_photo-1667340456421-e39b77a25217?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcwNzY1OTkwMg&ixlib=rb-4.0.3&q=80&w=1080",
      id: 2,
    },
    {
      url: "https://images.unsplash.com/photo-1606117331085-5760e3b58520?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1470&q=80",
      id: 3,
    },
    {
      url: "https://images.unsplash.com/photo-1704975986930-0c09f513c985?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcwNzY2MDY5Mw&ixlib=rb-4.0.3&q=80&w=1080",
      id: 4,
    },
  ];

  const clickNext = (ev: React.MouseEvent<HTMLSpanElement, MouseEvent>) => {
    ev.stopPropagation();
    if (activeIndex === imgArr.length - 1) {
      setActiveIndex(0);
    } else {
      setActiveIndex(activeIndex + 1);
    }
  };

  const clickPrev = (ev: React.MouseEvent<HTMLSpanElement, MouseEvent>) => {
    ev.stopPropagation();
    if (activeIndex === 0) {
      setActiveIndex(imgArr.length - 1);
    } else {
      setActiveIndex(activeIndex - 1);
    }
  };

  const clickDot = (
    ev: React.MouseEvent<HTMLLabelElement, MouseEvent>,
    id: number
  ) => {
    ev.stopPropagation();
    setActiveIndex(id);
  };

  /**
   * 현재 index에 따라 classname에 active 붙여주는 함수
   * id : img의 id
   */
  const indexClassName = (id: number) => {
    return id === activeIndex
      ? "carousel__img-box-active"
      : "carousel__img-box";
  };

  // 5초뒤에 이미지의 index +1 해주기
  useEffect(() => {
    const interval = setInterval(() => {
      setActiveIndex(() =>
        activeIndex === imgArr.length - 1 ? 0 : activeIndex + 1
      );
    }, 5000);
    return () => clearInterval(interval);
  }, [activeIndex]);

  return (
    <>
      <div className="carousel">
        <div className="carousel__controls">
          <span
            className="carousel__slide-prev"
            onClick={(ev) => clickPrev(ev)}
          >
            &lsaquo;
          </span>
          <span
            className="carousel__slide-next"
            onClick={(ev) => clickNext(ev)}
          >
            &rsaquo;
          </span>
        </div>

        <div className="carousel__img">
          {imgArr.map((img) => (
            <div className={indexClassName(img.id)} key={img.id}>
              <img src={img.url} alt={`img-${img.id}`} />
            </div>
          ))}
        </div>

        <div className="carousel__dots">
          {imgArr.map((el) => (
            <label
              key={el.id}
              onClick={(ev) => clickDot(ev, el.id)}
              className={
                activeIndex === el.id ? "carousel__dot-active" : "carousel__dot"
              }
            ></label>
          ))}
        </div>
      </div>
    </>
  );
}

css

/* carousel */
.carousel {
  width: 100%;
  height: 100%;
  position: relative;
}
.carousel:hover .carousel__controls {
  opacity: 1;
}
.carousel__controls {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 100px;
  line-height: 400px;
  color: #fff;
  padding: 0 10px;
  box-sizing: border-box;
  opacity: 0;
  transition: all 0.7s ease-in-out;
}

.carousel__controls span {
  cursor: pointer;
  opacity: 0.5;
}

.carousel__controls span:hover {
  opacity: 1;
}

.carousel__img {
  width: 100%;
  height: 100%;
  display: flex;
}

.carousel__img-box-active {
  max-width: 680px;
  width: 100%;
  height: 100%;
  display: flex;
  transition: opacity 0.8s ease-in-out;
}
.carousel__img-box {
  opacity: 0;
  flex: 1;
}

.carousel__img-box img,
.carousel__img-box-active img {
  width: 100%;
  height: 100%;
}

.carousel__dots {
  width: 100%;
  height: 40px;
  position: absolute;
  bottom: 15px;

  display: flex;
  gap: 7px;
  align-items: center;
  justify-content: center;
  opacity: 0.8;
}
.carousel__dot-active {
  width: 13px;
  height: 13px;
  background-color: #a4a1a1;
  border-radius: 50%;
  transition: background 0.5s ease-in-out;
}
.carousel__dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background-color: #fff;
}

0개의 댓글