styled-components 를 이용하여 메뉴 전환 효과주기

최경락 (K_ROCK_)·2022년 6월 1일
0

뭐임?

  • 대충 이런 걸 만들었습니다.
    → 이런 효과를 정확하게 뭐라고 하는지 모르겠다….
  • 유튜브를 돌아다니면서 최대한 비슷한 효과를 찾아보았고, css와 클래스를 이용하는 것만으로도 가능했지만…
  • styled-componentsprops 를 이용하여 만드는 방법이 더 간편 할 것 같아 해당 방법을 선택했고, 유튜브 영상에서 보았던 transform 속성을 활용하기로 했다.

개요

상태관리

  • 내가 어떤 메뉴를 클릭했고, 이동했는지에 대해 컴포넌트에 알려야 위치를 그에 맞게 이동 시켜 줄 수 있을 것이라 생각했다.
  • 그래서 상태를 만들어 현재 내가 위치해 있는 경로를 저장하여 관리하기로 했다.
const [currentPath, setCurrentPath] = useState<string>('');
  • 메뉴 버튼을 누를 때 마다 해당 상태를 갱신 시켜주었다.
  <Link to="todo" ref={todoBtn} onClick={() => setCurrentPath('/todo')}>
    todo
  </Link>
  <Link to="memory" ref={memoryBtn} onClick={() => setCurrentPath('/memory')}>
    memory
  </Link>
  • 새로고침이 발생한 경우에도 현재 위치를 가져오기 위해 useEffect 를 이용하여 상태를 갱신 시켜주었다.
useEffect(() => {
    setCurrentPath(window.location.pathname);
  }, []);

props 타입 지정하기

  • 하위 컴포넌트에 props 를 내려 줄 때 타입을 지정하는 것처럼, styled-components 의 컴포넌트에 props 를 내려 줄 때도 타입을 선언해주어야 한다.
const Indicator = styled.div<{ currentPath: string }>``;

첫 결과물

  • Indicator 라는 컴포넌트가 메뉴와 형제요소로 배치하고, position: absolute; 를 이용하여 메뉴 버튼의 뒤에 위치하게 했다.
  • 버튼이 두 개 이므로 부모요소의 너비 50% 만큼을 가지고, 전달받은 props 에 따라 그 위치를 transform : translateX() 를 이용하여 이동 시키는 방식을 사용하였다.
// styled-components

const Indicator = styled.div<{ currentPath: string }>`
  position: absolute;

  width: 50%;
  height: 100%;

  background-color: #000;
  border-radius: calc(1.5rem + 10px);

  z-index: -1;

  transition: 0.3s;

  transform: ${(props) => props.currentPath === '/todo' && 'translateX(0)'};
  transform: ${(props) =>
    props.currentPath === '/memory' && 'translateX(100%)'};
`;
// mark up

<div className="buttonWrapper">
  <Indicator currentPath={currentPath} />
  <SLink to="todo" ref={todoBtn} onClick={() => setCurrentPath('/todo')}>
    todo
  </SLink>
  <SLink to="memory" ref={memoryBtn} onClick={() => setCurrentPath('/memory')}>
    memory
  </SLink>
</div>;

수정

  • 이번엔 동일한 크기의 wrapper 를 메뉴와 형제요소로 배치시키고, 메뉴의 뒤에 위치 시킨 뒤 내부의 다른 요소를 인디케이터로 사용하는 방식을 사용했다.
  • 기존의 방식도 원하는 대로 동작하지만, 너비를 부모를 기준으로 %를 이용하여 지정하다 보니 메뉴의 수가 많아진다면 정확하지 않은 경우가 생길 수도 있다고 판단하였다.
  • 박스를 grid 를 이용하여 나누기 때문에, 혹시나 메뉴의 수가 늘어난다 해도, grid-template-columns 를 이용하여 수정해주면 원하는 너비만큼 인디케이터를 가져갈 수 있다.
    → 물론 적용하고자 하는 메뉴도 grid 여야 정확하겠지만…
  • 추가로 translateXcalc() 함수를 이용하여 계산하는 방식으로 수정하였다.
// styled-components

const IndicatorWrapper = styled.div<{ currentPath: string }>`
  position: absolute;

  display: grid;
  grid-template-columns: 1fr 1fr;

  width: 100%;
  height: 100%;

  z-index: -1;

  .indicator {
    height: 100%;
    background-color: #000;
    border-radius: calc(1.5rem + 10px);

    transition: 0.5s;

    transform: ${(props) =>
      props.currentPath === '/todo' && 'translateX(calc(100% * 0))'};
    transform: ${(props) =>
      props.currentPath === '/memory' && 'translateX(calc(100% * 1))'};
  }
`;
// mark up

<div className="buttonWrapper">
  <IndicatorWrapper currentPath={currentPath}>
    <div className="indicator" />
  </IndicatorWrapper>
  <Link to="todo" ref={todoBtn} onClick={() => setCurrentPath('/todo')}>
    todo
  </Link>
  <Link to="memory" ref={memoryBtn} onClick={() => setCurrentPath('/memory')}>
    memory
  </Link>
</div>;

+

  • 간단 할 줄 알았는데…. 생각보다 그렇지 못했다…!

0개의 댓글