import styled from 'styled-components';
import { AnimatePresence, motion } from 'framer-motion';
import { useState } from 'react';
const Wrapper = styled(motion.div)`
height: 100vh;
width: 100vw;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
`;
const Box = styled(motion.div)`
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 50px;
font-size: 24px;
width: 150px;
height: 150px;
background-color: rgba(255, 255, 255, 1);
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.06);
`;
const boxVariants = {
entry: (back : boolean) => ({
x: back ? -500 : 500,
opacity: 0,
scale: 0,
}),
animate: {
x: 0,
opacity: 1,
scale: 1,
transition: {
duration: 0.3,
},
},
exit: (back : boolean) => ({
x: back ? 500 : -500,
opacity: 0,
scale: 0,
rotateX: 0,
transition: {
duration: 0.3,
},
}),
};
function App() {
const [visible, setVisible] = useState(1);
const [back, setBack] = useState(false);
const slideNext = () => {
setBack(false);
setVisible((prev) => (prev === 5 ? 1 : prev + 1));
};
const slidePrev = () => {
setBack(true);
setVisible((prev) => (prev === 1 ? 5 : prev - 1));
};
return (
<Wrapper>
<AnimatePresence custom={ back }>
<Box
custom={ back }
variants={boxVariants}
initial="entry"
animate="animate"
exit="exit"
key={visible}
>
{visible}
</Box>
</AnimatePresence>
<button onClick={slidePrev}>prev</button>
<button onClick={slideNext}>next</button>
</Wrapper>
);
}
export default App;
배열을 매핑하지 않아도 AnimatePresence의 자식 요소 key를 바꿔주면 쉽게 슬라이드를 만들 수 있다. (key의 변경 -> 기존 요소는 사라지고(exit) 새 요소가 생성(entry))
custom : variants에 데이터를 보낼 수 있게 해주는 프로퍼티. custom 사용을 위해서는 variants 속성(위의 예에선 entry, exit)의 형식을 object를 return 하는 함수로 바꿔준다.
prev버튼을 누르고 next를 눌렀을 때 첫 exit 애니메이션 동작이 반대로 실행되는 문제. setBack()이 비동기로 값을 바꾸기 때문에 back이 변경되기전에 애니메이션이 먼저 실행되기 때문.
custom props 사용시 props(위 예에선 back)를 obj형태로 보낸다.
interface ICustomProps {
back: boolean;
}
const boxVariants = {
entry: ({ back }: ICustomProps) => ({
x: back ? -500 : 500,
opacity: 0,
scale: 0,
}),
animate: {
x: 0,
opacity: 1,
scale: 1,
transition: {
duration: 0.3,
},
},
exit: ({ back }: ICustomProps) => ({
x: back ? 500 : -500,
opacity: 0,
scale: 0,
rotateX: 0,
transition: {
duration: 0.3,
},
}),
};
function App() {
// 생략
return (
<Wrapper>
<AnimatePresence custom={{back}}>
<Box
custom={{ back }}
variants={boxVariants}
initial="initial"
animate="animate"
exit="exit"
key={visible}
>
{visible}
</Box>
</AnimatePresence>
<button onClick={slidePrev}>prev</button>
<button onClick={slideNext}>next</button>
</Wrapper>
)
}