-
Notifications
You must be signed in to change notification settings - Fork 0
Framer Motion
sarang_daddy edited this page Oct 3, 2023
·
1 revision
- React 애플리케이션에서 애니메이션을 쉽게 구현할 수 있는 라이브러리.
- props를 사용하여 애니메이션 효과를 적용할 수 있다.
npm install framer-motion
- motion 오브젝트에서 태그를 불로온다.
import { motion } from 'framer-motion';
<motion.div />;
- 이렇게 motion 키워드가 붙은 요소를
motion component
라고 한다.
const Box = styled(motion.div)`
width: 200px;
height: 200px;
background-color: white;
border-radius: 10px;
box-shadow:
0 2px 3px rgba(0, 0, 0, 0.1),
0 10px 20px rgba(0, 0, 0, 0.06);
`;
function App() {
return (
<Wrapper>
<Box />
</Wrapper>
);
}
- Framer Motion 컴포넌트는 다양한 props를 통해 애니메이션을 제어한다.
function App() {
return (
<Wrapper>
<Box transition={{ delay: 3 }} animate={{ borderRadius: '100px' }} />
</Wrapper>
);
}
- 간단한 애니메이션의 경우 animate props에서 직접 값을 설정할 수 있다.
<motion.div animate={{ x: 100 }} />
- initial: 애니메이션의 초기 상태를 설정한다.
- animate: 애니메이션의 최종 상태를 설정한다.
- transition: 애니메이션의 전환 효과를 설정한다.
<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],
}}
/>
- motion 컴포넌트가 처음 생성될 때, animate 속성에 적용된 값이 style 또는 initial 에 정의된 값과 다르다면
- animate 속성에 적용된 값으로 자동으로 애니메이션을 적용해 준다.
- 자동으로 적용하길 원치 않는다면 initㅑal 값을 false로 설정해 준다.
<motion.div animate={{ x: 100 }} initial={false} />
- 리액트에서는 컴포넌트가 트리에서 삭제될 경우 “즉시” 사라져버리기 때문에 사라지는 애니메이션을 적용하기 어렵다는 문제가 있다.
- AnimatePresence 컴포넌트를 사용하면 사라지는 애니메이션이 보여지는 동안 DOM에 유지되도록 할 수 있다.
const MyComponent = ({ isVisible }) => (
<AnimatePresence>
{isVisible && (
<motion.div
key="modal"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
)}
</AnimatePresence>
);
modal과 같은 반복적인 요소에는 추적가능한 key값이 필요하다.
2. Variants
- 단일 객체에 애니메이션을 구성하는 것은 animate props로 간단하다.
- 그런데 DOM 전체에 파생되는 애니메이션이나, 차례로 이뤄지는 애니메이션을 설정하고 싶을 때는 어떻게 해야할까?
- 그럴 때는 variants(변형)를 사용한다. 미리 정의한 애니메이션 세트라고 할 수 있다.
const variants = {
visible: (i) => ({
opacity: 1,
transition: {
delay: i * 0.3,
},
}),
hidden: { opacity: 0 },
};
return items.map((item, i) => (
<motion.li custom={i} animate="visible" variants={variants} />
));
- 만약 motion 컴포넌트에 자식 요소가 있다면, 자식 요소가 자체 animate 속성을 정의하기 전까지 variants의 변화를 상속받도록 할 수 있다.
- 쉽게 말해, variants에 정의한 속성명을 자식에게 그대로 물려줄 수 있다.
const list = {
hidden: { opacity: 0 },
visible: { opacity: 1 },
};
const item = {
hidden: { opacity: 0, y: -100 },
visible: { opacity: 1, y: 0 },
};
return (
<motion.ul initial="hidden" animate="visible" variants={list}>
<motion.li variants={item} />
<motion.li variants={item} />
<motion.li variants={item} />
</motion.ul>
);
- li에 달린 variants={item}은 initial="hidden"과 animate="visible"가 자동으로 적용된다.
Framer Motion은 다양한 제스처 기반의 애니메이션을 제공한다.
- Hover: 컴포넌트 위로 마우스가 올라갔을 때의 애니메이션
- Tap: 컴포넌트를 클릭했을 때의 애니메이션
- Drag: 컴포넌트를 드래그 했을 때의 애니메이션
<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 },
}}
// 클릭되면 동작
whileTap={{ scale: 0.9 }}
/>
- 레이아웃의 변경을 감지하고 자동으로 애니메이션 효과를 적용해준다.
- layout prop을 해당 컴포넌트에 추가하면 된다.
import { motion } from 'framer-motion';
function ExampleComponent({ isExpanded }) {
return (
<motion.div layout style={{ height: isExpanded ? 300 : 100 }}>
Content here
</motion.div>
);
}
- 위의 예제에서 isExpanded prop 값에 따라 높이가 변경되는데, layout prop으로 높이 변경 시 자연스러운 애니메이션 효과가 적용된다.
-
AnimateSharedLayout
및layoutId
를 사용하여 두 다른 컴포넌트 간의 레이아웃 애니메이션을 공유할 수 있다.
<Wrapper>
<Grid>
{['1', '2', '3', '4'].map((n) => (
<Box onClick={() => setId(n)} key={n} layoutId={n} />
))}
</Grid>
<AnimatePresence>
{id ? (
<Overlay
variants={overlay}
onClick={() => setId(null)}
initial="hidden"
animate="visible"
exit="exit"
>
<Box layoutId={id} style={{ width: 200, height: 200 }} />
</Overlay>
) : null}
</AnimatePresence>
</Wrapper>