Carousel 제작기

Sinf·2022년 7월 20일
0

고민의 흔적

목록 보기
33/38
post-thumbnail

Carousel

씬프로드를 제작하면서 여러가지 사진을 하나의 프레임에서 볼 수 있도록 구현할 필요가 있었다. 블로그 같이 사진 하나 하나 나열하고 글을 쓰는 형식이 아니라, 인스타그램과 같이 사진과 글을 따로 보여주는 형식이었기 때문에 하나의 프레임에서 사진을 옆으로 넘기는 형식이 필요했다.

옆으로 넘길 수 있기 때문에 slide라고 생각했는데,
Carousel이라는 명칭이 있다. 회전목마가 넘어가는 것과 같아서 그런건가?

어떻게 만들까?

일단, 기본적인 아이디어는 엘리스 SW 교육을 받을 때 들었던 강의에서 가져왔다.

  1. 보여주고자 하는 화면
  2. 가로로 길게 붙여진 사진들 (컨테이너)
  3. 사용자의 입력(왼쪽, 오른쪽 버튼 클릭)에 따라 움직이는 컨테이너

3가지를 조합해 Carousel을 만들 수 있다.

만들어보자

보여주는 화면

export default function Carousel() {
  return <div className="carousel"></div> 
}

보여지는 화면은 내부에 컨테이너를 가지는데, 넘치는 부분은 가려질 수 있도록 CSS에서 overflow에 대한 속성을 지정한다.

.carousel {
  overflow: hidden;
}

가로로 넘치는 부분을 hidden 속성으로 가린다.

내부의 컨테이너

컨테이너의 넓이를 보여지는 화면 너비 * 아이템 갯수로 지정해 좌우로 넓게 이어지도록 했다.

현재, 보여지는 화면의 너비는 좌우로 꽉찬 100%이기 때문에 100% * 아이템 갯수로 했다.

export default function Carousel() {
  const containerWidth = 100 * images.length;
  
  return <div className="carousel">
    <div 
      className="carousel__container"
      style={{ width: `${containerWidth}%`}}
    >
      { /* items */ }
    </div>
  </div>
}

내부의 컨테이너는 아이템들을 가로로 정렬하기 편하도록 flex 속성을 부여했다.

.carousel__container {
  display: flex;
}

이제 내부의 컨테이너에 아이템을 넣어주면 된다.
씬프로드는 내부에 이미지들을 넣어준다.
이미지의 너비는 컨테이너 너비 100%를 기준으로 1/N 씩 나눠가질 수 있도록 한다. (const itemWidth = 100 / images.length)

{
  images.map((image) => {
    <figure
      className="carousel__item"
      style={{ width: `${itemWidth}%` }}
    >
      <img className="carousel__img" /> 
    </figure>
  })
}

이미지를 다루다보면, 이미지를 감싸는 figure 태그 밖으로 이미지가 커지는 경우가 있다. 이미지 크기를 잡기 위해서 이미지 태그에 대해서 box-sizing: border-box; CSS 속성을 주면 이미지가 부모 태그 밖으로 커지지 않는다.

화면을 움직이자

화면을 움직이는 방식은 여러 방법이 있을 수 있다.
엘리스 SW 교육에서 사용한 방식을 margin-left를 활용한 방식이었다. 이 외에도 transform을 이용해서 컨테이너의 위치를 변경하는 것도 가능할 것 같다.

일단, margin-left를 이용해 옮기는 방식을 선택했다.
쉽게 구현할 수 있는 아이디어였기 때문에 선택했다.

이제 컨테이너의 margin-left를 tsx에서 변경할 수 있도록 컨테이너의 style 속성에 marginLeft 속성을 준다.

export default function Carousel() {
  const containerWidth = 100 * images.length;
  const [leftMargin, setLeftMargin] = useState(0);
  
  return <div className="carousel">
    <div 
      className="carousel__container"
      style={{
        width: `${containerWidth}%`,
        marginLeft: `${leftMargin}%`
      }}
    >
      { /* items */ }
    </div>
  </div>
}

margin-left는 변화에 따라 상태를 추적해야 하기 때문에 useState로 값을 관리한다.

이제 사용자의 입력에 따라 leftMargin의 값이 변화하도록 코드를 구현한다.

Carousel의 좌측과 우측에 버튼을 구현하고, 우측에 있는 버튼을 클릭할 경우 margin-left의 값을 음수로 만들어 보여지는 화면에서 컨테이너가 다음 그림과 같이 되도록 한다.

반대로 좌측에 있는 버튼을 클릭한다면 margin-left에 값을 더해 돌아오도록 한다.

부드럽게 움직이자.

지금과 같이 구현한다면, 화면은 짠, 짠, 짠 하고 변하게 된다.
하지만 회전목마와 같은 Carousel은 옆으로 이동하는 모습을 보여준다.

이를 위해 컨테이너의 CSS 속성에 transition 속성을 부여한다.

.carousel__container {
  transition: margin ease 0.5s;
}

transition을 사용하면 부드럽게 넘어가게 된다.

완성본

Carousel.tsxCarousel.css에서 전체 코드를 확인할 수 있다.

이슈

이미지가 아래로 정렬되는 문제

처음 Container에 이미지를 넣을 때, 이미지를 크기에 맞추면 아래로 정렬되고, 정렬을 중간으로 맞추려고 하면 부모 태그 사이즈보다 커지는 문제가 발생했다.

생각보다 단순하게 해결할 수 있었다.

이미지를 감싸는 figure 태그에 flex 속성을 주고,
img에는 box-sizing: border-box; object-fit: contain; 속성을 통해 해결할 수 있었다.

flex 박스로 지정하면 잘 정렬된 형태로 나오고,
box-sizing 속성을 통해 이미지가 부모 태그 밖으로 커지지 않도록 하고, contain으로 화면을 꽉채워줬더니 해결되었다.

이상한 문제 발생 드르륵 드르륵

처음 Carousel을 완성했을 때, 이미지가 넘어갈 때 드르륵 드르륵 화면이 떨리는 현상이 있었다.

해당 문제는 scroll-bar가 생겼다가 사라졌다가 하면서 생기는 문제였다.

scroll-bar가 보이지 않도록 설정하고 해결했다.

html::-webkit-scrollbar {
  display: none;
}
profile
주니어 개발자입니다. 🚀

0개의 댓글