bkdragon's log

Carousel 본문

UI UX

Carousel

bkdragon 2023. 9. 12. 21:55

Carousel 은 슬라이드쇼를 말한다. 회전목마라는 뜻을 가지고 있다.

 

이벤트와 같은 것들을 광고하는 용도로 사용되는 것을 종종 볼 수 있다.

 

예시

  1. 커머스 (ex. 쿠팡)
    • 상품의 사진을 Carousel을 통해 보여준다.
  2. Netflix
    • 인기 작품, 추천 작품을 Carousel을 통해 보여준다.

 

장점

  1. 눈에 띈다. 주목하게 되는 효과가 있다.
  2. 효율적인 공간활용. 페이지를 넘기지 않고도 다양한 정보를 제공할 수 있다.
  3. 다양한 variation 이 있다. 자동으로 돌아가게 한다던지 다음 요소를 약간 노출한다던지, 버튼을 통해 다음 요소로 넘긴다던지 등이 있다.

 

단점

  1. 웹 접근성 문제가 생길 수 있다.
  2. 성능 저하. 웹 페이지 로딩 속도에 영향을 미칠 수 있다.
  3. 다양한 variation. 이는 단점이 될 수도 있다고 생각한다.
  4. 이미지의 크기나 비율로 인해 디자인 일관성 측면에도 문제가 생길 수 있다.

 

구현

버튼을 통해 이전과 다음 요소를 의도적으로 볼 수 있고 dot 요소를 통해 현재 이미지가 몇번째인지 확인 가능하고 auto는 설정을 통해 적용할 수도 적용하지 않을 수도 있게 구현해보려고 한다.

 

Carousel.tsx

import React, { useState, useEffect, useCallback } from 'react';

import styles from './carousel.module.scss';

import classNames from 'classnames/bind';

const cn = classNames.bind(styles);

interface Props {
  images: string[];
  interval?: number;
}

const Carousel: React.FC<Props> = ({ images, interval }) => {
  const [current, setCurrent] = useState<number>(0);

  const next = useCallback(() => {
    if (current < images.length - 1) {
      setCurrent((prev) => prev + 1);
    } else {
      setCurrent(0);
    }
  }, [images.length, current]);

  const prev = useCallback(() => {
    if (current === 0) {
      setCurrent(images.length - 1);
    } else {
      setCurrent((prev) => prev - 1);
    }
  }, [images.length, current]);

  const clickDot = (index: number) => {
    setCurrent(index);
  };

  useEffect(() => {
    if (!interval) return;

    const timer = setInterval(next, interval);
    return () => {
      clearInterval(timer);
    };
  }, [interval, next]);

  return (
    <div className={cn('container')}>
      {images.map((item, index) => (
        <img
          src={item}
          key={index}
          className={cn('item')}
          style={{ transform: `translateX(${current * -800}px)` }}
          alt="a"
        />
      ))}
      <button className={cn('button')} onClick={prev}>
        {'<'}
      </button>
      <button className={cn('button', 'button_right')} onClick={next}>
        {'>'}
      </button>
      <div className={cn('dot_wrapper')}>
        {images.map((_, index) => (
          <div
            key={index}
            className={cn('dot', { dot_active: current === index })}
            onClick={() => clickDot(index)}
          />
        ))}
      </div>
    </div>
  );
};

export default Carousel;

 

  • Carousel에서 보여줄 이미지의 리스트는 부모로부터 받아오고 있다.
  • interval 값이 있으면 auto로 움직인다. useEffect를 통해서 현재 index 값을 지속적으로 증가시켜주는 식으로 구현했다.
  • 현재 index 값에 따라 보여지는 이미지가 결정된다. 각 이미지는 Container(부모 div)를 가득채우는 크기이고 현재 index 값에 따라 translate 값이 변경되어 이미지가 움직인다.
  • 각 이미지에는 flex : 0 0 100% 가 적용되어 있는데 flex-shrink 가 0가 임으로 container와 관계없이 고정 폭이 된다.

 

상태를 사용하니 이미지 이동을 간단하게 구현할 수 있었다. 다만 아쉬운 점은 인라인 스타일을 통해 translate를 변경해주고 있는 부분인데 scss를 사용하니 상태와 엮어서 할 수 있는 방법이 저것 말곤 없었다(일단은). styled-components를 사용하면 좀 더 선언적으로 구현이 가능하긴하다.

 

 

잘생기고 멋진 공유님 이미지로 사용해보았다!

'UI UX' 카테고리의 다른 글

flex : 1, overflow : auto issue !  (1) 2024.07.24
Button (with Chakra)  (0) 2023.08.25
제출 폼  (0) 2023.08.23
드롭 다운 메뉴  (0) 2023.08.21
아코디언 메뉴  (0) 2023.08.13