Framer Motion
✔️AnimatePresence
- 공식문서는 여기
- 컴포넌트가 React 트리에서 제거될 때 애니메이션 효과를 줄 수 있다.
- React에는 다음과 같은 수명 주기 메서드가 없기 때문에 종료 애니메이션을 활성화한다.
- 규칙
AnimatePresence
내부에 조건문이 있어야 사용이 가능하다
- 사용 방법
import { motion, AnimatePresence } from "framer-motion"
export const MyComponent = ({ isVisible }) => (
<AnimatePresence>
{isVisible && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
)}
</AnimatePresence>
)
exit
- 이 컴포넌트가 트리에서 제거될 때 애니메이션할 대상입니다.
- 사용방법을 알았으니, 연습해보자!
- click 버튼을 클릭하면 나타났다 사라지는 박스에
AnimatePresence
을 적용하여 애니메이션을 줄 것!
const boxVariants = {
initial : { opacity: 0},
visible: { opacity: 1 },
leaving : { opacity: 0 }
}
function App() {
const [ showing, setShowing ] = useState(false);
const toggleShowing = () => setShowing(prev => !prev);
return (
<button onClick={toggleShowing}> Click! </button>
<AnimatePresence>
{ showing ? (
<Box
variants={boxVariants}
initial="initial"
visible="visible"
exit="leaving" // element 가 사라질때 동작할 애니메이션
/>
) : null}
</AnimatePresence>
);
};
- initial
- visible
- exit (ㅋㅋ 서서히 opacity:0 이 되며 사라지는 중.. 캡처..)
✔️AnimatePresence 을 사용하여 BOX Slider 구현하기 ⭐⭐⭐
- 넷플릭스 클론코딩의 중요 포인트 ~~! 박스 슬라이드 구현하기 연습 타임
- 중요도가 있으니 ㅇㅇ 만큼 별 세개 주었다 그럼 렛츠고
function App() {
return (
<AnimatePresence>
{[1, 2, 3, 4, 5, 6, 7, 8, 9 10].map( i => <Box key={i}>{i}</Box>}
<AnimatePresence>
)
}
- 우선 10개의 박스를 만들어줬다
- 이제, 한번에 한 box 씩 보여주자
function App() {
const [visible, setVisivle] = useState(1);
return (
<AnimatePresence>
{[1, 2, 3, 4, 5, 6, 7, 8, 9 10].map( i =>
i === visible ? <Box key={i}>{i}</Box> : null
}
<AnimatePresence>
)
}
- 이제, i 를 1씩 증가시켜주는 기능을 만들자
const boxVar = {
invisible : { x: 500, opacitiy: 0, scale: 0 },
visible: { x: 0, opacitiy: 1, scale: 1 },
exit : { x: -500, opacitiy: 0, scale: 0 }
}
function App() {
const [visible, setVisivle] = useState(1);
const nextPlease = () => setVisivle(prev => prev === 10 ? 10: prev +1 )
return (
<AnimatePresence>
{[1, 2, 3, 4, 5, 6, 7, 8, 9 10].map( i =>
i === visible ? <Box
variants={box}
initial="invisible"
animate="visible"
exit="exit"
key={i}
>
{i}
</Box> : null
}
<AnimatePresence>
<button onClick={nextPlease}> Next </button>
)
}
prev
가 10과 같으면 10을 return 하고, 아니라면 prev
을 1씩 증가시키고,
array 의 숫자가 visible state 와 같다면, 같은 것만 보여줌
- next 버튼을 클릭하면
visible
state 의 값이 1씩 증가하면서,
오른쪽(x:500) 에서 박스가 오고, visible 상태이면 중앙에(x:0), 사라질땐 왼쪽(x:-500) 으로 사라진다.
- visible state 가 1에서 2로 증가한다는 것은, 첫번째 박스인 1이 사라지고 2가 새로 생성된다는 뜻이다. 그러므로
AnimatePresence
에 의해 exit
애니메이션이 발생한다.
- ㅋㅋ이렇게 된다
- 다음으로 가는 버튼만 있으니 이전으로 돌아가는 기능(1씩 감소)도 만들어주자
const boxVar = {
invisible : { x: 500, opacitiy: 0, scale: 0 },
visible: { x: 0, opacitiy: 1, scale: 1 },
exit : { x: -500, opacitiy: 0, scale: 0 }
}
function App() {
const [visible, setVisivle] = useState(1);
const nextPlease = () => setVisivle(prev => prev === 10 ? 10: prev +1 )
const prevPlease = () => setVisivle(prev => prev === 1 ? 1: prev -1 )
return (
<AnimatePresence>
{[1, 2, 3, 4, 5, 6, 7, 8, 9 10].map( i =>
i === visible ? <Box
variants={box}
initial="invisible"
animate="visible"
exit="exit"
key={i}
>
{i}
</Box> : null
}
<AnimatePresence>
<button onClick={nextPlease}> Next </button>
<button onClick={prevPlease}> Prev </button>
)
}
- 그런데 이렇게하면, 1씩 감소하긴 하는데.. 증가할때와 똑같이 오른쪽에서 왼쪽으로 나오는 애니메이션이 동일해서 햇갈린다.
- 감소할때는 왼쪽에서 나와서 오른쪽으로 사라지게 하고,
visible state 를 사용해 1~10개의 박스를 렌더링해오는 코드를 축소시켜보자.
const boxVar = {
invisible : { x: 500, opacitiy: 0, scale: 0 },
visible: { x: 0, opacitiy: 1, scale: 1 },
exit : { x: -500, opacitiy: 0, scale: 0 }
}
function App() {
const [visible, setVisivle] = useState(1);
const nextPlease = () => setVisivle(prev => prev === 10 ? 10: prev +1 )
const prevPlease = () => setVisivle(prev => prev === 1 ? 1: prev -1 )
return (
<AnimatePresence>
<Box
variants={box}
initial="invisible"
animate="visible"
exit="exit"
key={visible}
>
{visible}
</Box>
}
<AnimatePresence>
<button onClick={nextPlease}> Next </button>
<button onClick={prevPlease}> Prev </button>
)
}
visible
state 가 1부터 시작하여 1씩 증가하고 1씩 감소하니, key 에 부여하자.
그럼 react js 는 key 값이 바뀔때마다 새로운 컴포넌트가 생겼다고 생각해, 컴포넌트를 리렌더해주고
그 과정에서 컴포넌트가 삭제될때 AnimatePresence
를 통해 애니메이션이 실행된다.
- 이제 방향을 바꿔줄 차례
✔️custom
- 각 애니메이션 컴포넌트에 대해 동적 variants를 다르게 적용할 때 사용할 수 있는 사용자 지정 데이터.
- 즉, variants 에 데이터를 보낼 수 있게 해주는 프로퍼티이다.
- 사용 방법
const variants = {
visible: (custom) => ({
opacity: 1,
transition: { delay: custom * 0.2 }
})
}
< motion.div custom={0} animate="visible" variants={variants} />
< motion.div custom={1} animate="visible" variants={variants} />
< motion.div custom={2} animate="visible" variants={variants} />
- 나의 경우 가고자 하는 방향에 따라서 invisible 과, exit 만 바꿔주면 되므로,
custom
을 사용해 위치를 바꿔주자
const boxVar = {
invisible : (isback:boolean) => ({ // back state & custom 의 값
x: isback ? -500 : 500, // 1이 감소한다면 왼쪽, 증가한다면 오른쪽에서 나타남
opacitiy: 0,
scale: 0,
})
visible: { x: 0, opacitiy: 1, scale: 1 },
exit : (isback:beelean) => ({ // back state & custom 의 값
x: isback ? 500 : -500, // 1이 감소한다면 오른쪽으로, 증가한다면 왼쪽으로 사라짐
opacitiy: 0,
scale: 0
})
}
function App() {
const [visible, setVisivle] = useState(1);
const [back, setBack] = useState(false);
const nextPlease = () => {
setBack(false);
setVisivle(prev => prev === 10 ? 10: prev +1 );
}
const prevPlease = () => {
setBack(true);
setVisivle(prev => prev === 1 ? 1: prev -1 );
}
return (
<AnimatePresence mode="wait" custom={back}> <<<----
<Box
custom={back} <<<----
variants={box}
initial="invisible"
animate="visible"
exit="exit"
key={visible}
>
{visible}
</Box>
}
<AnimatePresence>
<button onClick={nextPlease}> Next </button>
<button onClick={prevPlease}> Prev </button>
)
}
<AnimatePresence mode="wait">
true
로 설정하면 AnimatePresence
는 한 번에 하나의 컴포넌트만 랜더링한다.
exiting중인 컴포넌트는 entering하는 컴포넌트가 렌더링되기 전에 exit 애니메이션을 완료합니다.
- 이렇게 하면 !!!
- next 버튼을 눌러 visible state 가 1씩 증가하면 back state 가
false
값을 가지므로 왼쪽에서 오른쪽으로 나타나고
- prev 버튼을 눌러 visible state 가 1씩 감소하면 back state 가
true
값을 가지므로 오른쪽에서 왼쪽으로 사라진다.