[React] Framer Motion

·

3 min read

오늘은 리액트의 애니메이션 라이브러리중 하나인 Framer Motion에 대해 작성해보겠다.

Framer Motion을 사용하게 된 계기는, 방학때 진행했던 CATch Mind라는 디자이너 협업 퀴즈 게임 개발을 하다가, 연속적으로 나오는 애니메이션 구현을 위해 사용했다. 예를 들어, 캐릭터가 진화하는 장면에서, 기존의 캐릭터가 화면 아래로 내려가고 새로운 캐릭터가 올라와야 했다. 마치 포켓몬스터 진화하는 장면처럼 말이다. 이를 위해서는 DOM에서 지워질때 애니메이션을 적용했어야 했는데, React가 State로 Component를 관리할 때, 즉시 DOM을 업데이트 해서, 사라질때 애니메이션 구현이 어려웠다. 이를 해결해주는 기깔난 라이브러리를 찾았는데, 그게 Framer Motion이였다.

Framer Motion 공식 사이트

https://www.framer.com/motion/

프로젝트 주소(반응형이 아니고, IPad 12Pro 기준임, 방학때 반응형으로 고칠 예정)

https://catch-project.vercel.app/


설치 및 사용 방법

  • node.js 다운로드하기 - Javscript Run time

  • npm install framer-motion

  • import {motion} from"framer-motion"

  • <motion.div> ...codes </motion.div>

    • <motion.div>를 적용시키고 싶은 JSX에 붙여주면 된다.

사용 방법

기본적으로 <motion.div>의 props로 값들을 전달해서 사용한다.

  1. animate prop

    : 적용시킬 애니메이션을 넣는 곳이다. Css 스타일에 Base를 두었다.

  2. transition prop

    : 애니메이션이 어떻게 적용될지 결정한다.

    ex) duration, ease, repeat etc

     // translatex 100 for 2 seconds in easeOut
     <motion.div
       animate={{ x: 100 }}
       transition={{ ease: "easeOut", duration: 2 }}
     />
    

    지원하는 CSS Animations

    • Translate shortcuts: x, y, z

    • Translate: translateX, translateY, translateZ

    • Scale: scale, scaleX, scaleY

    • Rotate: rotate, rotateX, rotateY, rotateZ

    • Skew: skew, skewX, skewY

    • Perspective: transformPerspective

지원하는 ValueTypes

  • Numbers: 0, 10 etc.

  • Strings containing numbers: "0vh", "10px" etc.

  • Colors: Hex, RGB, HSLA.

  • Complex strings containing multiple numbers and/or colors (ie "10px 0 #000")

  1. initial prop

    : 처음 렌더링될때 상태를 의미한다. 즉, DOM에 처음 렌더링 될때의 초기 상태를 정의해줄 수 있다.

  2. exit prop

    : 컴포넌트가 렌더링 해제(?) 즉, DOM에서 삭제될때 최종 상태를 정의할 수 있다.

    2개의 Props를 통해, 리액트의 단점인 DOM에서 컴포넌트가 삭제될때의 애니메이션을 구현할 수 있다.

     <AnimatePresence>
       // isVisible state를 통해, DOM Element를 관리한
       {isVisible && (
         // 안보임(등장전) > 보임(등장) > 안보임(퇴장)
         <motion.div
           key="modal"
           initial={{ opacity: 0 }}
           animate={{ opacity: 1 }}
           exit={{ opacity: 0 }}
         />
       )}
     </AnimatePresence>
    
    • 추가로, <AnimatePresence>라는 컴포넌트로 감싸주어야 한다.

      import { motion,AnimatePresence} from "framer-motion"

    • AnimatePresence 에는 key값을 설정해주어야 DOM Tree에서 track할 수 있다한다.

cf) 만약, animate 속성의 객체에 정의된 값과 기본 값(처음 렌더링 된 상태)이 다르면, animate가 실행된다. initial = {false}를 통해, 이를 방지할 수 있다.


그런데, 만약 여러 코드가 같은 애니메이션을 사용한다면 계속 initial, aniamte를 작성해야 할까? 귀찮다. 이를 위해서, variants 라는 기능을 제공한다. 또한, variants는 부모 자식간의 Orchestration (연속적 애니메이션 재생)을 가능하게 한다.

먼저, 기본 사용법을 알아보자.

variants라는 객체에 2개의 key값에 최초 상태, 최종 상태를 정의해주면 된다.

const variants = {
  hidden: {
    backgroundColor: "#fff",
    transition: { duration: 2 }
  },
  visible: {
      backgroundColor: "#f00"
  }
}
// 초기 : inactive 상태, 등장 후 :active 상태,
<motion.div 
    variants={variants},
    initial="hidden",
    animate="visible" />

variant의 중요한 특징은 자식이 자신만의 애니메이션을 정의하지 않으면, 자식으로 애니메이션이 전파된다는 것이다.

예제를 통해 알아보자.

const list = {
  visible: { opacity: 1 },
  hidden: { opacity: 0 },
}

const item = {
  visible: { opacity: 1, x: 0 },
  hidden: { opacity: 0, x: -100 },
}

return (
  <motion.ul
    initial="hidden"
    animate="visible"
    variants={list}
  >
    // li 는 list variant를 상속받지만, item variant로 덮어씀
    <motion.li variants={item} />
    <motion.li variants={item} />
    <motion.li variants={item} />
  </motion.ul>
)

내가 매력적이라 느꼈던 다음 기능은, 부모 자식간 애니메이션 연속 설정(Orchestration) 기능이다. 가끔은, 부모이후에 일정시간을 두고 자식의 애니메이션을 실행했어야 했다. 이를 위해서, delay를 두고 힘들게 만들었어야 했는데, Framer-Motion은 코드 한줄로 이를 가능하게한다.

코드로 이해해보자.

 const list = {
  visible: {
    opacity: 1,
    transition: {
      when: "beforeChildren",      
    },
  },
  hidden: {
    opacity: 0,
    transition: {
      when: "afterChildren",
    },
  },
}
return (
  <motion.ul
    initial="hidden"
    animate="visible"
    variants={list}
  >
    // li 는 ul이 끝나면 자동으로 실행된다. 우와~
    <motion.li/>
    <motion.li/>
    <motion.li/>
  </motion.ul>
)

내가 썻던 기능들은 이런 것들이다. 사실 hover, scroll 등 이외에도 많은 기능이 있다. 거의 겉면만 핥은 수준이다. 다음에, 프로젝트에서 사용하게 된되면 이어서 써보겠다. 쓰지도 않을 것을 공부하는건 낭비가 아닌가!!??
edited by 김지호