<motion.div> 형식으로 사용해야한다.const Box = styled(motion.div) 이렇게 사용{/* initial은 초기값 지정 */}
<Box transition={
{type:"spring", bounce:0.5}} initial={{scale:0}} animate={{scale:1, rotate:360}
}>
Motion</Box>
const myVars = {
start:{scale:0},
end:{scale:1, rotate:360,transition:{type:"spring", bounce:0.}}
}
<Box variants={myVars} initial="start" animate="end">Motion</Box>
//==================================================================
const boxVars = {
start:{
opacity:0,
},
end:{
opacity:1,
transition: {
type: "spring",
//하위 컴포넌트들에게 0.5 딜레이를 해준다
delayChildren:0.5,
//하위 컴포넌트들에게 각각 딜레이 0.5s씩 더해주는것
staggerChildren:0.5
}
}
}
{/* 상위 컴포넌트가 variants를 가지고 있다면 하위 컴포넌트에게 initial, animation을 똑같이 주고 있는 상태다. */}
<Box variants={boxVars} initial="start" animate="end">
{/* 상위 컴포넌트에서 받아온 initial,animate작성은 생략 가능
하위 컴포넌트의 객체 속성값도 상위에서 받아오는 값이랑 동일하게 작성한다
*/}
<Circle variants={circleVars}/>
</Box>
//==================================================================
//biggerBoxref을 사용해 Box컴포넌트가 BiggerBox의 밖으로 나가지 않게 만든다.
const boxVars = {
hover:{scale:1.5,rotate:90},
tap:{scale:1,borderRadius:"100px"},
drag:{backgroundColor:"rgb(46,204,113)",transition:{duration:0.8}
}
const biggerBoxref = useRef(null)
return(
{/* while~ 마우스로 인해 발생되는 이벤트
whileDrag안의 backgroundColor를 사용할때는 rgb값을(숫자) 넣어야 애니메이션 효과 할 수 있다.
*/}
<BiggerBox ref={biggerBoxref}>
{/* dragSnapToOrigin은 drag해도 컴포넌트가 원래 위치로 돌아가게 만들어준다 */}
<Box drag dragSnapToOrigin variants={boxVars} whileHover="hover" whileTap="tap" dragConstraints={biggerBoxref} />
</BiggerBox>
)
//ReactJs에 영향을 주지 않기에 값이 바뀌어도 컴포넌트 리렌더링 되지 않는다.
const x = useMotionValue(0)
//갑이 변경된걸 실시간으로 확인하고 싶을때 사용
useMotionValueEvent(x,"change",(i) => {console.log('x',i)})
{/* 위치가 변경될때마다 변경된 값을 확인하기위해 선언한 x 를 style에 넣어준다*/}
<Box style={{x}} drag dragSnapToOrigin />
//원하는 좌표의 값들을 먼저 입력해주고, 좌표값마다 받아올 값을 뒤에 적어준다
//입력값과 출력값 개수가 같아야함
const scale = useTransform(x,[-800,0,800],[2,1,0.1])
{/* 위치가 변경될때마다 변경된 값을 확인하기위해 선언한 x 를 style에 넣어준다*/}
<Box style={{x,scale}} drag="x" dragSnapToOrigin />
//Box의 (useMotionValue)x값을 사용해 Wrapper backround변경
const x = useMotionValue(0)
const gradient = useTransform(x,[-800,0,800],[
"linear-gradient(135deg, rgb(0,210,238), rgb(0,83,238))",
"linear-gradient(135deg, rgb(238,0,153), rgb(221,0,238))",
"linear-gradient(135deg, rgb(0,238,155), rgb(238,178,0))"
])
<Wrapper style={{background:gradient}}>
<Box style={{x,rotate}} drag="x" dragSnapToOrigin />
</Wrapper>
);
//scrollY는 픽셀 scrollYProgress는 0 부터 1까지
const {scrollY,scrollYProgress} = useScroll()
useMotionValueEvent(scrollY,"change",(i) => {console.log('scrollY',i)})
useMotionValueEvent(scrollYProgress,"change",(i) => {console.log('scrollYProgress',i)})
//스크롤에 따른 scale변화주는 useTransform사용
const scale = useTransform(scrollYProgress,[0,1],[1,30])
const svg = {
start:{
pathLength:0,
fill:"rgba(255,255,255,0)"
},
end:{
pathLength:1,
fill:"rgba(255,255,255,1)",
}
}
<Svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<motion.path
variants={svg}
initial="start"
animate="end"
//property 각각 transition을 따로 줄 수 있다.
//default는 모든 property에 적용되는값
//여기서 default는 pathLength에 해당하는 duration
transition={{
default:{duration:5},
fill:{duration:2,delay:2}
}}
stroke="white" strokeWidth="2" fill="transparent" d="..."/>
</Svg>
const boxVars = {
start:{
opacity:0,
scale:0
},
end:{
opacity:1,
scale:1,
rotate:360
},
leaving:{
opacity:0,
y:20
}
}
const [show,setShow] = useState(false)
const toggleButton = () => setShow(pre => !pre)
<button onClick={toggleButton}>Click</button>
{/* AnimatePresence안에는 항상 조건문이 들어가 있어야 한다.
exit는 animate가 사라질때 나타날 효과를 설정해준다
*/}
<AnimatePresence>
{show ? <Box variants={boxVars} initial="start" animate="end" exit="leaving"/> : null}
</AnimatePresence>
const boxVariants = {
entry: (isBack: boolean) => ({
x: isBack ? -500 : 500,
opacity: 0,
scale: 0,
rotateX: -180,
}),
center: {
x: 0,
opacity: 1,
scale: 1,
rotateX: 0,
transition: {
duration: 1,
},
},
exit: (isBack: boolean) => ({
x: isBack ? 500 : -500,
opacity: 0,
scale: 0,
rotateX: 360,
transition: {
duration: 1,
},
}),
};
function App() {
const [visible, setVisible] = useState(1);
const [isBack, setIsBack] = useState(false);
const nextPlease = () => {
setIsBack(false);
setVisible((prev) => (prev === 10 ? 10 : prev + 1));
};
const prevPlease = () => {
setIsBack(true);
setVisible((prev) => (prev === 1 ? 1 : prev - 1));
};
return (
<Wrapper>
//custom사용시에는 AnimatePresence와 컴포넌트 둘 다 적어준다
<AnimatePresence custom={isBack}>
<Box
//custom을 사용하면 boxVariants에 property를 전달 할 수 있다.
custom={isBack}
variants={boxVariants}
initial="entry"
animate="center"
exit="exit"
key={visible}
>
{visible}
</Box>
</AnimatePresence>
<button onClick={prevPlease}>Prev</button>
<button onClick={nextPlease}>Next</button>
</Wrapper>
);
}
const [click,setClick] = useState(false)
const toggleClick = () => setClick(per => !per)
<Box style={{
justifyContent: click ? "center" : "flex-start",
alignItems: click ? "center" : "flex-start"
}}>
{/* 외부(상위컴포넌트)의 의해 css로 변화되는 컴포넌트는 layout를 사용하면 css가 animate효과를 갖게한다. */}
<Circle layout/>
</Box>
//click에의해 서로 보이고 안보여지는 효과를 layoutId를 사용해 같은 값을 넣어주면 animate효과로 해준다.
<Box>
{!click ? <Circle layoutId="circle"/> : null}
</Box>
<Box>
{click ? <Circle layoutId="circle"/> : null}
</Box>
const Box = styled(motion.div)`
background-color: rgba(255, 255, 255, 1);
border-radius: 40px;
height: 200px;
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.06);
`;
const Overlay = styled(motion.div)`
width: 100%;
height: 100%;
position: absolute;
display: flex;
justify-content: center;
align-items: center;
`;
const Grid = styled.div`
display: grid;
grid-template-columns: repeat(3, 1fr);
width: 50vw;
gap: 10px;
div:first-child,
div:last-child {
grid-column: span 2;
}
`;
const AniOverlay = {
start:{backgroundColor:"rgba(0,0,0,0)"},
end:{backgroundColor:"rgba(0,0,0,0.7)"},
exit:{backgroundColor:"rgba(0,0,0,0)"}
}
<Grid>
{[1,2,3,4].map(i =>
<Box key={i} layoutId={i+""} onClick={() => setId(i+"")}>{i}</Box>)
}
</Grid>
<AnimatePresence>
{id ?
<Overlay onClick={() => setId(null)}
variants={AniOverlay}
initial="start"
animate="end"
exit="exit">
<Box style={{width:400,height:200}} layoutId={id+""}>{id}</Box>
</Overlay>
: null}
</AnimatePresence>
여태까지 animation을 사용하기 위해선 css으로 작업을 해왔는데 FramerMotion을 배우면서 너무 간편하다고 생각했다. css으로 animation을 만들면 코드도 길어지고 노가다의 연속이었는데 라이브러리를 사용하니 너무 간편하고 좋은거같다. 잘만 사용하면 정말 간편하게 팝업들도 만들 수 있고 뚝뚝 끊어지는 정적인코드를 동적인코드로 만들 수 있는 부분이 좋은거같다.