Framer Motion의 Custom 속성을 활용한 동적 애니메이션 구현

sarang_daddy·2023년 10월 21일
0

React

목록 보기
23/26

React에서 Framer Motion 라이브러리로 애니메이션 효과를 적용하던 중 의도치 않은 버그가 발생되었다. 어떻게 버그가 해결되었는지 그리고 왜 이런 버그가 발생했는지 기록하고 정리해보자.

🐛 슬라이더 애니메이션 효과 버그 발생

  • 반대 방향으로 슬라이더를 전환하면 이전 슬라이더 페이지가 사라지는 방향이 이전 속성 기반으로 움직이는 버그가 발생.

❌ 문제 코드

const [direction, setDirection] = useState<'right' | 'left'>('right');

<S.Slider>
      <AnimatePresence initial={false} onExitComplete={toggleLeaving}>
        <S.SliderControl>
          <S.StyledIcon
            onClick={() => handleSliderPage(ControlKeys.LEFT)}
            icon="chevron-left"
            size="3x"
          />
          <S.StyledIcon
            onClick={() => handleSliderPage(ControlKeys.RIGHT)}
            icon="chevron-right"
            size="3x"
          />
        </S.SliderControl>
        <S.Row
          variants={rowVariants[direction]}
          initial="hidden"
          animate="visible"
          exit="exit"
          transition={{ type: 'tween', duration: 1 }}
          key={sliderPage}
        >

export const rowVariants = {
  left: {
    hidden: { x: -window.innerWidth },
    visible: { x: 0 },
    exit: { x: window.innerWidth },
  },
  right: {
    hidden: { x: window.innerWidth },
    visible: { x: 0 },
    exit: { x: -window.innerWidth },
  },
};
  • 방향 상태를 만들고 이벤트가 일어나면 해당 상태를 애니메이션 속성으로 선택
    - 오른쪽 버튼을 누르면 variants의 right를 선택
    - 왼쪽 버튼을 누르면 variants의 left를 선택

✅ 해결 코드

  • AnimatePresence의 custom 속성 개념을 확인
  • custom을 이용하여 motion의 애니메이션에 prop(사용자 정의값)을 전달
const [direction, setDirection] = useState<'right' | 'left'>('right');

<S.Slider>
      <AnimatePresence
        initial={false}
        custom={direction}
        onExitComplete={toggleLeaving}
      >
        <S.SliderControl>
          <S.StyledIcon
            onClick={() => handleSliderPage(ControlKeys.LEFT)}
            icon="chevron-left"
            size="3x"
          />
          <S.StyledIcon
            onClick={() => handleSliderPage(ControlKeys.RIGHT)}
            icon="chevron-right"
            size="3x"
          />
        </S.SliderControl>
        <S.Row
          custom={direction}
          variants={rowVariants}
          initial="hidden"
          animate="visible"
          exit="exit"
          transition={{ type: 'tween', duration: 1 }}
          key={sliderPage}
        >

export const rowVariants = {
  hidden: (direction: string) => ({
    x: direction === 'right' ? window.innerWidth : -window.innerWidth,
  }),
  visible: { x: 0 },
  exit: (direction: string) => ({
    x: direction === 'right' ? -window.innerWidth : window.innerWidth,
  }),
};
  • 버그가 사라지도 의도하던 애니메이션이 잘 적용되고 있다.

📝 결론

  • 두 코드의 차이점은 S.Row 컴포넌트에서 variants prop을 어떻게 사용하는가가 다르다.

첫 번째 코드

  • 첫 번째 코드에서 rowVariantsdirection 값에 따라 다른 객체를 반환한다.
  • S.Row 컴포넌트에서 variants prop은 rowVariants[direction]을 통해 직접 해당 객체를 가져온다.
  • 즉, S.Row 컴포넌트가 리렌더링될 때마다 variants prop의 참조(객체)가 변경된다.

두 번째 코드

  • 두 번째 코드에서는 custom prop을 통해 direction 값을 전달하고 rowVariants가 함수를 통해 값을 계산한다.
  • 동일한 객체에서 값을 다르게 계산한다.
  • S.Row 컴포넌트의 variants prop은 rowVariants를 직접 참조하고 있다.
  • 즉, S.Row 컴포넌트가 리렌더링되더라도 variants prop의 참조(객체)는 동일하여 추적이 된다.

Framer Motion은 내부적으로 애니메이션 상태를 추적하기 위해 참조를 사용한다.
동적으로 애니메이션의 속성을 변경하고자 할때 객체를 할당하면 리렌더링마다 새로운 객체로 참조하게된다.
따라서, 객체의 참조는 일정하게 유지하면서 동적인 값을 주기 위해 custom 속성을 이용한다.

profile
한 발자국, 한 걸음 느리더라도 하루하루 발전하는 삶을 살자.

0개의 댓글