framer-motion : react를 위한 motion & gesture 라이브러리
framer-motion
는 리액트에서 애니메이션과 제스쳐를 쉽게 다룰 수 있도록 해주는 라이브러리다. animate
props에 값을 세팅하면 CSS transitions
를 자동생성하는 방법으로 애니메이션을 만들어주고, drag나 hover 등의 제스쳐를 지원해주기도 하고, 단일 애니메이션 prop으로 하위 트리까지 이어지는 애니메이션을 적용할 수도 있다.
설치
npm install framer-motion
import { motion } from 'framer-motion';
<motion.div animate={{ scale: 0.5 }} />;
motion은 다음과 같이 설치하고 선언해서 사용할 수 있다.
motion components
motion.div
, motion.svg
이렇게 html 요소나 svg 요소에 대응할 수 있는 컴포넌트로 다른 부분은 스태틱 요소와 동일하나 애니메이트, 제스쳐 등을 설정할 수 있다.
<motion.div
animate={{
x: 0,
backgroundColor: '#000',
boxShadow: '10px 10px 0 rgba(0, 0, 0, 0.2)',
position: 'fixed',
transitionEnd: {
display: 'none',
},
}}
/>
transform
속성으로 CSS 애니메이션을 구현하면 브라우저가 GPU를 활용하기 때문에 left
대신 x
옵션을 사용하는 것이 더 빠르다고 한다.
// GPU accelerated (fast)
<motion.div style={{ x: 0 }} animate={{ x: 100 }} />
// CPU drawing (slower)
<motion.div style={{ left: 0 }} animate={{ left: 100 }} />
사용예시
Animation
<motion.div animate={{ scale: 2 }} />
animate
prop는 값을 객체로 받고 이 값이 지정되면 첫 렌더링시에, 또는 변경되면 변경된 값으로 motion 컴포넌트를 애니메이팅 한다. 예를 들면 위의 motion 컴포넌트는 div 엘레먼트로 렌더링 되면서 두 배로 확대된다.
Keyframes
<motion.div
animate={{
scale: [1, 2, 2, 1, 1],
rotate: [0, 0, 270, 270, 0],
borderRadius: ['20%', '20%', '50%', '50%', '20%'],
}}
/>
값을 array로 넣게 되면 순서대로 실행된다. 기본적으로는 동일한 시간으로 실행되나 transition
프로퍼티를 조정해서 시간값을 수정할 수 있다.
Variants
const [isOpen, setIsOpen] = useState(false)
<motion.nav
animate={isOpen ? "open" : "closed"}
variants={{
open: { opacity: 1, x: 0 },
closed: { opacity: 0, x: "-100%" },
}}
>
<Toggle onClick={() => setIsOpen(!isOpen)} />
<motion.ul variants={{
open: { /* */ },
closed: { /* */ },
}}
/>
</motion.nav>
variants
props를 활용하면 선언적 방법으로 돔 전체에 전파되는 애니메이션을 만들 수도 있다. 예를 들면 위와 같은 컴포넌트에서는 animate
가 open
일 경우 motion.nav
와 motion.ul
의 variants
값 중 open
값들이 실행된다.
Gesture
<motion.button whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }} />
motion 컴포넌트는 whileHover
이나 whileTap
같은 제스처 헬퍼 props를 가지고 있다.
Drag
const dragAreaRef = useRef(null)
<motion.div ref={dragAreaRef} />
<motion.div drag dragConstraints={dragAreaRef} />
위과 같은 방법으로 드래그가 가능한 요소를 지정할 수 있고 드래그 가능한 영역도 ref
를 넘겨주거나 top
, left
등을 특정 값으로 넘겨서 지정할 수 있다.
MotionValues
const x = useMotionValue(0);
const background = useTransform(
x,
[-100, 0, 100],
['#ff008c', '#7700ff', 'rgb(230, 255, 0)'],
);
return (
<motion.div style={{ background }}>
<motion.div drag="x" dragConstraints={{ left: 0, right: 0 }} style={{ x }}>
<Icon x={x} />
</motion.div>
</motion.div>
);
motion은 MotionValue
를 애니메이션을 제어하는데 사용하는데 이것을 훅을 통해 직접 사용할 수도 있다. 위의 코드에서는 내부 motion 컴포넌트를 드래그 했을때 x가 변경되면서 background 색상도 변경된다.
Viewport scroll
const { scrollYProgress } = useViewportScroll();
const scale = useTransform(scrollYProgress, [0, 1], [0.2, 2]);
return (
<motion.div style={{ scale }}>
<motion.div
style={{
scaleY: scrollYProgress,
}}
/>
</motion.div>
);
useViewportScroll
훅은 스크롤 관련 MotionValues
를 제공하므로 이를 motion 컴포넌트와 결합해서 활용할 수 있다.
정리
여기에 적기엔 너무 많은 기능이 있는 라이브러리라 다 적기가 어렵다. 자세한 기능은 라이브러리의 공식 docs를 참고하면 좋을 것 같다. 프론트엔드 개발을 하면서도 css 애니메이션은 다소 단순한 트랜지션만 적용하곤 했는데, 이런 라이브러리를 활용하면 상태관리나 렌더링에 대해 덜 신경쓰면서 유저에게 더 감각적인 ui/ux를 제공할 수 있지 않을까 싶다. 이런 라이브러리들을 보면 수준 높은 프론트엔드 개발을 하려면 js도 중요하지만 css를 더 잘 알아야겠다고 생각하게 되는 것 같다ㅠㅠ