React, Typescript: 카드 슬라이더 (점 모양)

hotbreakb·2022년 5월 21일
2
post-custom-banner

➡️ 이후 포스팅: 슬라이더 위치 수정

청년을 구해줘!에서 메인 페이지 카드 슬라이더를 만들고 있다. 눌렀을 때 어떻게 이동시킬지 굉장히 고민이 많았다🤔 카드를 어떻게 옮기지? 싶었다.

카드

하나의 카드 안에는 이미지, 텍스트, 하트 수가 들어간다.

카드의 왼쪽과 오른쪽에 검은색 선이 나타난다. div를 겹쳤을 때 나타나는 현상이다.
이를 해결할 수 있는 방법은 3가지이다.

  1. 사진의 너비를 줄인다.
    • 자존심이 상한다.
    • 줄인 나는 크기가 안 맞는 게 훤히 보인다. 불편하다.
  2. ::after를 쓴다.
  3. 부분별로 잘라서 컴포넌트를 만든다.
    • 디자이너 제이미와 합의된 내용
const Card = ({ backgroundImg = tmpImg, title, likeNum }: CardProps) => {
  let navigate = useNavigate();
  return (
    <StyledCard onClick={() => navigate('/detail')} backgroundImg={backgroundImg}>
      <StyledTitleContainer>
        <StyledTitle>{title}</StyledTitle>
        <StyledLikeNumContainer>
          <BlueHeart />
          <StyledLikeNum>{likeNum}</StyledLikeNum>
        </StyledLikeNumContainer>
      </StyledTitleContainer>
    </StyledCard>
  );
};

(styled-components를 쓰면 이름이 길어지고 태그 색이 다 똑같아서 분간하기 어렵다.)

카드 슬라이더

Card를 갖고 있는 CardSlider를 만든다.

카드 여러 개

  • StyledCardConatiner: 슬라이더를 포함하는 body width: 100%
  • StyledCardSlider: 카드 길이만큼 길다. 이 컴포넌트가 StyledCardConatiner보다 길다.
  • Card: 카드 하나
  return (
    <section>
      <StyledCardConatiner>
        <StyledCardSlider
          slideIndex={slideIndex}
          todaySubscriptionsLength={todaySubscriptions.length}
        >
          {todaySubscriptions.map((todaySubscription) => (
            <Card
              key={todaySubscription.id}
              title={todaySubscription.houseName}
              likeNum={todaySubscription.likeNum}
            />
          ))}
        </StyledCardSlider>
      </StyledCardConatiner>

점의 개수는 가져온 데이터의 개수와 같게 만들었다.

      <StyledDotsContainer>
        {Array.from({ length: todaySubscriptions.length }).map((item, index) => (
          <StyledDot
            key={`${index}-${item}`}
            active={slideIndex === index}
            onClick={() => onDotClick(index)}
          />
        ))}
      </StyledDotsContainer>

점 누르면 카드 움직이기

점 하나를 누를 때마다 slideIndex state를 변경해서, 이 state에 비례하게 움직이게 한다. Card 자체가 움직이는 것이 아니라 StyledCardSlider가 움직인다.

const StyledCardSlider = styled.div<{ slideIndex: number; todaySubscriptionsLength: number }>`
  height: 100%;
  display: flex;
  transition: all 1s ease;
  transform: translateX(
    ${(props) =>
      props.slideIndex * -CARD_WIDTH + (CARD_WIDTH * (props.todaySubscriptionsLength - 1)) / 2}vw
  );
`;

${(props) => props.slideIndex * -CARD_WIDTH 이렇게 하면 슬라이더가 화면 중앙에서 시작한다. 반면 지금 나와있는 코드처럼 작성하면 슬라이더의 시작 위치가 맨 앞으로 바뀐다.

이것보다는 Image와 Dot을 분리하여 같은 선에 두고 absoulte, relative의 관계로 위치를 잡은 다음에 transform을 지정하는 것이 좋다. 다 만들어뒀는데 브랜치를 지워버렸다... 😇

참고로 calc *을 계산할 때는 px이 여러 번 들어가면 계산이 되지 않는다.
예) calc(24px * 3) 🙆‍♀️ // calc(24px * 3px) 🙅‍♀️

스크롤

카드 위에 움직였을 때 스크롤하면 transformX가 바뀐다.

StyledCardSlider를 포함하고 있는 StyledCardConatiner 컴포넌트에 overflow-x: scroll을 추가하여 스크롤 기능을 만든다. -webkit-overflow-scrolling는 모바일 화면에서 스크롤이 잘 동작하지 않아서 추가하였다. none; 3가지는 스크롤을 숨기는 것이다.

const StyledCardConatiner = styled.div`
  width: 100%;
  overflow-x: scroll; // PC
  -webkit-overflow-scrolling: touch; // mobile
  -ms-overflow-style: none; /* IE and Edge */
  scrollbar-width: none; /* Firefox */

  &::-webkit-scrollbar {
    display: none; /* Chrome, Safari, Opera*/
  }
`;

스크롤될 때마다 active된 dot의 색을 변경시킬 수 있다. 지금 이 상태에서 하는 것보다 absoulte, relative의 관계로 바꿔서 하는 게 훨씬 편하다. 아니면 수식이 아주 더러워진다. 스크롤되었을 때 sliceIndex를 바꾸고, dot이 눌렸을 때만 transformX가 변경되게 하면 된다.

코드

profile
글쟁이 프론트 개발자, 헬렌입니다.
post-custom-banner

0개의 댓글