
공식문서 ☝☝☝
$ npm i framer-motion
import { motion } from "framer-motion"
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1, rotateZ: 360 }}
/>
처음 애니메이션 시작 상태를 정의합니다.
해당 요소가 최종적으로 어떻게 바뀔지 정의합니다.
애니메이션의 delay 혹은 duration 등을 정의할 수 있습니다.
<motion.div
initial={{ opacity: 0, scale: 0.5 }}
animate={{ opacity: 1, scale: 1 }}
transition={{
duration: 0.8,
delay: 0.5,
ease: [0, 0.71, 0.2, 1.01]
}}
/>
리액트에서는 컴포넌트가 트리에서 삭제될 경우 “즉시” 사라져버리기 때문에 사라지는 애니메이션을 적용하기 어렵다는 문제가 있습니다.
하지만 AnimatePresence 컴포넌트를 사용하면 사라지는 애니메이션이 보여지는 동안 DOM에 유지되도록 할 수 있습니다.
import "./styles.css";
import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
export default function App() {
const [value, setValue] = useState("false");
return (
<div className="App">
<button onClick={() => setValue(true)}>보여주기</button>
<button onClick={() => setValue(false)}>사라지기</button>
<AnimatePresence>
{value && (
<motion.div
className="box"
initial={{ scale: 0 }}
animate={{ scale: 1, rotateZ: 360 }}
exit={{ scale: 0, rotateZ: 0 }}
transition={{
duration: 0.8,
delay: 0.5,
ease: [0, 0.71, 0.2, 1.01]
}}
/>
)}
</AnimatePresence>
</div>
);
}
animate의 값을 배열로 설정하면 Motion이 각 값을 차례로 처리합니다. 현재값을 초기 키프레임으로 사용하고 싶다면 null 값을 주면 됩니다.
이렇게 하면 애니메이션 되는 도중에 애니메이션이 시작되더라도 전환이 자연스러워집니다.
<AnimatePresence>
{value && (
<motion.div
className="box"
animate={{
scale: [1, 2, 2, 1, 1],
rotate: [0, 0, 180, 180, 0],
borderRadius: [
"0%", "0%", "50%", "50%", "0%"
]
}}
transition={{
duration: 2,
ease: "easeInOut",
times: [0, 0.2, 0.5, 0.8, 1],
repeat: Infinity,
repeatDelay: 1
}}
/>
)}
</AnimatePresence>
여기서 times 를 배열로 설정하면 내가 원하는
시간을 마음대로 조정할 수 있습니다.
rpeat 은 몇번 반복할건지, repeatDelay 는 애니메이션이 끝난후 몇초 기다릴 건지 정의합니다.
hover, tap, drag, focus, inView 등의 제스처가 시작될 때 값 집합에 애니메이션을 적용할 수도 있습니다.
<motion.div
initial={{ opacity: 0.2 }}
whileInView={{
opacity: 1,
rotate: [0, 360],
borderRadius: ["20%", "50%"],
transition: { delay: 0.05 }
}}
whileHover={{
scale: 1.2,
transition: { type: "spring", stiffness: 400, damping: 10 }
}}
/>
whileHover ={{}}
whileInView ={{}}
등등이 있습니다.
DOM 전체에 파생되는 애니메이션을 사용하고 싶을때 쓰입니다.
즉 여러 motion.div 나 다른 motion.li.. 등등 과 같은 곳에서 공통적으로 쓰고 싶은 요소가 있을때 유용합니다.
const variants = {
hidden: { opacity: 0 },
visible: { opacity: 1 },
}
<motion.div
initial="hidden"
animate="visible"
variants={variants}
/>
만약 motion 컴포넌트에 자식 요소가 있다면, 자식 요소가 자체 animate 속성을 정의하기 전까지 variants의 변화를 상속받도록 할 수 있습니다.
쉽게 말해, variants에 정의한 속성명을 자식에게 그대로 물려줄 수 있습니다.
export default function App() {
const list = {
hidden: { opacity: 0 },
visible: { opacity: 1 }
};
const item = {
hidden: { opacity: 0, y: 100 },
visible: { opacity: 1, y: 0 }
};
return (
<div className="wrap">
<motion.ul variants={list} initial="hidden" animate="visible">
<motion.li variants={item}>item 1</motion.li>
<motion.li variants={item}>item 2</motion.li>
<motion.li variants={item}>item 3</motion.li>
</motion.ul>
</div>
);
}
위의 예제에서 볼 수 있는 것처럼, item에 달린 애니메이션은 모두 동시에 시작됩니다.
하지만 transition에 추가적인 속성을 더해 자식 애니메이션의 실행을 조정할 수 있습니다.
export default function App() {
const list = {
hidden: {
opacity: 0
},
visible: {
opacity: 1,
transition: {
when: "beforeChildren",
staggerChildren: 0.2
}
}
};
const item = {
hidden: { opacity: 0, y: 50 },
visible: { opacity: 1, y: 0 }
};
return (
<div className="wrap">
<motion.ul variants={list} initial="hidden" animate="visible">
<motion.li variants={item}>item 1</motion.li>
<motion.li variants={item}>item 2</motion.li>
<motion.li variants={item}>item 3</motion.li>
</motion.ul>
</div>
);
}
함수를 정의해서 각 variant에 동적으로 애니메이션을 설정할 수도 있습니다.
이러한 variant 함수들은 컴포넌트의 custom 속성으로 넘어오는 값을 인자로 받습니다.
export default function App() {
const variants: {} = {
hidden: {
opacity: 0.2,
y: 15
},
visible: (i: number) => ({
opacity: 1,
y: 0,
transition: {
delay: i * 0.2,
duration: 1,
repeat: Infinity,
repeatType: "reverse"
}
})
};
const items = ["️❤️", "💛", "💜"];
return (
<div className="wrap">
<ul>
{items.map((item, i) => (
<motion.li
key={item}
initial="hidden"
animate="visible"
variants={variants}
custom={i}
>
{item}
</motion.li>
))}
</ul>
</div>
);
}
대부분의 UI 인터랙션에 맞춰 착착 애니메이션이 실행되지만,
좀더 복잡한 시퀀스를 구현하고 싶다면 useAnimationControls 훅으로 애니메이션을 수동 시작/중지할 수 있습니다.
import { useEffect, useState } from "react";
import { motion, useAnimationControls } from "framer-motion";
export default function App() {
const [show, setShow] = useState(false);
const controls = useAnimationControls();
useEffect(() => {
if (show) {
controls.start({ scale: 6 });
}
}, [controls, show]);
return (
<div className="wrap">
<motion.h1 animate={controls}>{show ? "Wow!" : "..."}</motion.h1>
<button onClick={() => setShow(true)}>setShow(true)</button>
</div>
);
}