Motion Animation

단순히 CSS만으로는 간단한 애니메이션을 구현할때에도 매우 많은 코드를 작성해야 됩니다. motion (구 framer-motion)는 간단한 코드들로 빠르게 애니메이션 효과를 구현할 수 있습니다.




탭 애니메이션

motion에서는 layoutId를 사용하여 깉은 요소들을 연결시켜서 자연스러운 레이아웃을 제공합니다.
활성 컴포넌트를 표시하는 motion.spanlayoutId를 설정하여 각 탭 item들을 연결시킨 뒤 transition={{ type: "spring", bounce: 0.2, duration: 0.6 }} 애니메이션 효과를 적용하여 부드럽게 전환되는 애니메이션을 구현할 수 있습니다.




모핑 카드

motion1
App Store

Native App에서는 위와같은 모핑 카드가 많이 사용됩니다. 모핑 카드를 사용하면 부드러운 애니메이션을 통해 사용자에게 더 나은 경험을 제공할 수 있습니다.

탭 애니메이션과 같이 layoutId를 사용하여 이미지와 텍스트를 연결시킵니다.

카드가 클릭되어 선택 상태로 변경될 때, motion.div의 layoutId를 활용하여 카드의 크기와 위치 변화에 애니메이션을 추가하고, transition={{ layout: { duration: 0.4, ease: "easeInOut" }}}로 애니메이션의 지속 시간과 속도 변화를 설정해줍니다.

텍스트부분의 경우, 카드가 확장된 후에 AnimatePresence를 initial={{ opacity: 0 }}로 초기 상태를 설정하고, animate={{ opacity: 1 }}로 애니메이션을 적용했습니다.




드래그 앤 드롭

DnD기능은 여러 라이브러리로 구현할 수 있지만, motion을 사용하여 커스텀하기 쉽게 구현할 수 있습니다.

<Reorder.Group axis="y" onReorder={setItems} values={items}>
  {items.map((item) => (
    <Reorder.Item key={item} value={item}>
      {item}
    </Reorder.Item>
  ))}
</Reorder.Group>

motion에서는 <Reorder.Group />으로 <Reorder.Item />을 감싸서 각 아이템들을 DnD기능을 구현할 수 있습니다. axis="y"로 드래그 방향을 설정하고, onReorder로 해당 아이템의 변경될때의 상태를 관리할 수 있습니다.


const Item = ({ item }: ItemProps) => {
  const y = useMotionValue(0);
  const dragControls = useDragControls();

  return (
    <Reorder.Item value={item} style={{ y }} dragControls={dragControls}>
      <div
        drag
        dragControls={dragControls}
        onPointerDown={(event) => dragControls.start(event)}
      >
        <span>{item}</span>
      </div>
    </Reorder.Item>
  );
};

useDragControls 훅으로 각 아이템을 제어 할 수 있습니다. drag 속성을 추가하여 해당 요소를 드래그할 수 있도록 설정하고, onPointerDown 이벤트로 드래그를 시작하도록 설정합니다.

useMotionValue 훅으로 y값을 설정하여 드래그시에 y값을 변경하도록 설정합니다.




오버레이 컴포넌트

다음 컴포넌트는 간단한 서랍장 컴포넌트입니다. motion을 사용하면 다양한 overlay 컴포넌트를 쉽게 구현할 수 있습니다.

Doodles #5608

버튼을 클릭할때 isOpen 상태를 변경하여 AnimatePresence안의 motion.divinitialanimate를 사용하여 애니메이션을 적용하였습니다.

위와 같이 간단한 오버레이 컴포넌트를 만들때 다양한 효과를 넣어서 좀더 사용자에게 나은 경험을 제공할 수 있습니다.

이와 같이 여러 효과를 CSS 코드로 넣으면 다음과 같이 많은 작성해야하며, 각 HTML 요소에 상태별로 클래스를 추가해야합니다.

.drawer-enter {
  opacity: 0;
  transform: scale(0.9) translateY(50px) translateX(-50%);
}

.drawer-enter-active {
  opacity: 1;
  transform: scale(1) translateY(0) translateX(-50%);
  transition: opacity 0.2s ease-out, transform 0.2s ease-out;
}

.drawer-exit {
  opacity: 1;
  transform: scale(1) translateY(0) translateX(-50%);
}

.drawer-exit-active {
  opacity: 0;
  transform: scale(0.9) translateY(50px) translateX(-50%);
  transition: opacity 0.2s ease-out, transform 0.2s ease-out;
}

이는 매우 번거롭고, 유지보수가 어렵습니다. 아래와 같이 motion을 사용하면 간단한 코드로 애니메이션을 구현할 수 있습니다.

<motion.div
  initial={{ opacity: 0, scale: 0.9, y: 50, x: "-50%" }}
  animate={{ opacity: 1, scale: 1, y: 0, x: "-50%" }}
  exit={{ opacity: 0, scale: 0.9, y: 50, x: "-50%" }}
  transition={{ duration: 0.2, ease: "easeOut" }}
/>