프론트엔드 개발자가 될거야./react

stacked card slider 구현하기(react, styled-component)

정니정은니 2023. 5. 11. 12:59

 

디자이너님에게 아래에 있는 card slider처럼  stacked card slider를 구현해달라는 요청을 받았다.

Stripe Sessions 2023 | Stripe Sessions Conference San Francisco

그동안 슬라이드를 구현할 때 react-slick 라이브러리로 구현했는데 stacked card slider는 react-slick로 구현할 수 없었다.

react-slick의 verticalSwipeToSlide를 활용해볼까도 했지만 라이브러리가 너무 망가져서 슬라이더가 오히려 이상해지기만 했다.

구글링을 통해 codepen에서 html, css, jQuery로 구현된 좋은 예시를 발견했고

이 코드를 내 코드로 녹여 구현할 수 있었다.
https://codepen.io/akramurridjal/pen/VwwrGVY

 

Stacked card slider JS

...

codepen.io

 

- 내가 구현한 stacked card slider

3.5초마다 자동으로 다음장으로 넘어가며 슬라이더를 클릭하거나 아래의 dot을 클릭해도 넘어간다.

 

 

- stacked card slider 구현기

나는 카드가 총 3개여서 그 3개의 카드를 구현하기 위해 

  const [indexes, setIndexes] = useState<number[]>([0, 1, 2]);
  const images = [StakingImg.ad1, StakingImg.ad2, StakingImg.ad3];

일단 이렇게 indexs state를 만들고 그 index에 맞게 images에서 알맞은 이미지를 꺼내오는 로직을 생각했다.

<CardsWrapper onClick={handleClick}>
  {[0, 1, 2].map((i) => (
    <Card key={i} index={images[i]} data-slide={indexes.indexOf(i)} />
  ))}
</CardsWrapper>

이런식으로 indexs를 map으로 돌려 index props에 넣어주고
사용자 지정 속성인 data-slide에도 indexes 배열에서 각 카드의 위치를 나타내는 인덱스를 찾아 data-slide 속성으로 할당하여
각 카드가 현재 표시되는 위치를 알 수 있게 했다.
data-slide 속성은 각 카드의 위치를 식별하는 데 사용된다.

1. 쌓여진 카드 레이아웃 구현하기

data-slide 속성은 CSS 선택자에서도 사용될 수 있는데 쌓여진 카드 레이아웃을 구현하기 위해 나는 이렇게 사용했다.

 &[data-slide="0"] {
    transform: translate(0px, 0px) scale(1);
    z-index: 4;
    opacity: 1;
  }

  &[data-slide="1"] {
    transform: translate(0px, -35px) scale(0.9);
    z-index: 3;
    opacity: 0.6;
  }

  &[data-slide="2"] {
    transform: translate(0px, -70px) scale(0.8);
    z-index: 2;
    opacity: 0.3;
  }

 

2. 슬라이드 카드를 다음 순서로 이동시키기

handleClick 함수는 슬라이드 카드를 다음 순서로 이동시키는 함수이다.

현재 인덱스 배열인 indexes를 변경하여 각 카드의 순서를 업데이트해준다.

indexes 배열을 스프레드 연산자로 새로운 배열에 복사하고 첫 번째 인덱스를 shift 메서드를 사용하여 삭제한 다음,
그 인덱스를 마지막 위치에 추가하는 로직이다.
이렇게 인덱스 배열을 업데이트하면, 카드가 순서대로 이동하게 되는 것이다.

  const handleClick = () => {
    setIndexes((indexes: number[]) => {
      const newIndexes = [...indexes];
      const firstIndex = newIndexes.shift();
      newIndexes.push(firstIndex!);

      return newIndexes;
    });
  };

  useEffect(() => {
    const interval = setInterval(() => {
      handleClick();
    }, 3500);

    return () => clearInterval(interval);
  }, []);