so woon!

[FRAMER MOTION] layout, layoutId 사용하기 본문

Framer Motion/개념정리

[FRAMER MOTION] layout, layoutId 사용하기

xowoony 2023. 5. 16. 17:46

학습일 : 2023. 05. 16


layout 사용하기


element에 layout prop을 주게 되면
element의 layout이 바뀔 때
알아서 animate가 된다.

만약 css 때문에 layout이 바뀐다면
알아서 animation이 만들어질 것이다.


   

  <Box
    style={{
      justifyContent: clicked ? "center" : "flex-start",
      alignItems: clicked ? "center" : "flex-start",
    }}
  >


위 코드대로라면 center부터 flex-start까지의 animation 말이다.



Circle component에 layout 을 써보도록 하겠다.

import { styled } from "styled-components";
import { motion } from "framer-motion";
import { useState } from "react";

// style
const Wrapper = styled(motion.div)`
  height: 100vh;
  width: 100vw;
  align-items: center;
  display: flex;
  flex-direction: column;
  justify-content: center;
  background: linear-gradient(135deg, rgb(252, 191, 248), rgb(21, 151, 203));
`;

const Box = styled(motion.div)`
  width: 400px;
  height: 400px;
  background-color: rgba(255, 255, 255, 1);
  border-radius: 40px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.1);
  display: flex;
`;

const Circle = styled(motion.div)`
  background-color: #00a5ff;
  width: 100px;
  height: 100px;
  border-radius: 50px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.1);
`;

function App() {
  const [clicked, setClicked] = useState(false);
  const toggleClicked = () => setClicked((prev) => !prev);
  return (
    <Wrapper onClick={toggleClicked}>
      <Box
        style={{
          justifyContent: clicked ? "center" : "flex-start",
          alignItems: clicked ? "center" : "flex-start",
        }}
      >
        <Circle layout />
      </Box>
    </Wrapper>
  );
}
export default App;



실행결과




Framer Motion은 무언가 외부의 힘에 의해 바뀐 것을 감지한다.
뭔가를 간단하게 animate 해주고 싶으면
layout을 써주면 될 것으로 보인다.

 


layoutId 사용하기

(shared layout animation)




첫번째 박스안 원이 클릭을 안했을 때 원이 나타나고
클릭을 했을 때 원이 사라지게
그리고 그걸 계속 반복할 수 있게 한다.

import { styled } from "styled-components";
import { motion } from "framer-motion";
import { useState } from "react";

// style
const Wrapper = styled(motion.div)`
  height: 100vh;
  width: 100vw;
  align-items: center;
  display: flex;
  flex-direction: row;
  justify-content: center;
  background: linear-gradient(135deg, rgb(252, 191, 248), rgb(21, 151, 203));
`;

const Box = styled(motion.div)`
  width: 300px;
  height: 300px;
  background-color: rgba(255, 255, 255, 1);
  border-radius: 40px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.1);
  align-items: center;
  display: flex;
  flex-direction: row;
  justify-content: center;
`;

const Circle = styled(motion.div)`
  background-color: #00a5ff;
  width: 50px;
  height: 50px;
  border-radius: 50px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.1);
`;

function App() {
  const [clicked, setClicked] = useState(false);
  const toggleClicked = () => setClicked((prev) => !prev);
  return (
    <Wrapper onClick={toggleClicked}>
      <Box>
        {!clicked ? <Circle /> : null}
      </Box>
      <Box>
        <Circle />
      </Box>
    </Wrapper>
  );
}
export default App;



실행결과





나머지 박스안 원에도 적용해본다 (반대로)

import { styled } from "styled-components";
import { motion } from "framer-motion";
import { useState } from "react";

// style
const Wrapper = styled(motion.div)`
  height: 100vh;
  width: 100vw;
  align-items: center;
  display: flex;
  flex-direction: row;
  justify-content: center;
  background: linear-gradient(135deg, rgb(252, 191, 248), rgb(21, 151, 203));
`;

const Box = styled(motion.div)`
  width: 300px;
  height: 300px;
  background-color: rgba(255, 255, 255, 1);
  border-radius: 40px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.1);
  align-items: center;
  display: flex;
  flex-direction: row;
  justify-content: center;
`;

const Circle = styled(motion.div)`
  background-color: #00a5ff;
  width: 50px;
  height: 50px;
  border-radius: 50px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.1);
`;

function App() {
  const [clicked, setClicked] = useState(false);
  const toggleClicked = () => setClicked((prev) => !prev);
  return (
    <Wrapper onClick={toggleClicked}>
      <Box>
        {!clicked ? <Circle /> : null}
      </Box>
      <Box>
      {clicked ? <Circle /> : null}
      </Box>
    </Wrapper>
  );
}
export default App;



실행결과
영상3

 



여기서 왼쪽 박스안 Circle과 오른쪽 박스안 Circle이
같은 component라고 Framer에게 말해주어야 한다.
즉 <Circle /> 두개를 연결해야 한다는 뜻이다.


    <Wrapper onClick={toggleClicked}>
      <Box>
        {!clicked ? <Circle /> : null}
      </Box>
      <Box>
      {clicked ? <Circle /> : null}
      </Box>
    </Wrapper>



연결하기 위해서는
두 Circle component 안에
layoutId 라는 prop을 추가하면 되고,
두개 다 같은 Id를 작성해주면 된다.


import { styled } from "styled-components";
import { motion } from "framer-motion";
import { useState } from "react";

// style
const Wrapper = styled(motion.div)`
  height: 100vh;
  width: 100vw;
  align-items: center;
  display: flex;
  flex-direction: row;
  justify-content: center;
  background: linear-gradient(135deg, rgb(252, 191, 248), rgb(21, 151, 203));
`;

const Box = styled(motion.div)`
  width: 300px;
  height: 300px;
  background-color: rgba(255, 255, 255, 1);
  border-radius: 40px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.1);
  align-items: center;
  display: flex;
  flex-direction: row;
  justify-content: center;
`;

const Circle = styled(motion.div)`
  background-color: #00a5ff;
  width: 50px;
  height: 50px;
  border-radius: 50px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.1);
`;

function App() {
  const [clicked, setClicked] = useState(false);
  const toggleClicked = () => setClicked((prev) => !prev);
  return (
    <Wrapper onClick={toggleClicked}>
      <Box>
        {!clicked ? <Circle layoutId="circle" /> : null}
      </Box>
      <Box>
      {clicked ? <Circle layoutId="circle" /> : null}
      </Box>
    </Wrapper>
  );
}
export default App;




실행결과
Framer가 둘을 연결해주고 animation을 만들게 된다.

 



스타일도 바꿔볼 수 있다.


import { styled } from "styled-components";
import { motion } from "framer-motion";
import { useState } from "react";

// style
const Wrapper = styled(motion.div)`
  height: 100vh;
  width: 100vw;
  align-items: center;
  display: flex;
  flex-direction: row;
  justify-content: center;
  background: linear-gradient(135deg, rgb(252, 191, 248), rgb(21, 151, 203));
`;

const Box = styled(motion.div)`
  width: 300px;
  height: 300px;
  background-color: rgba(255, 255, 255, 1);
  border-radius: 40px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.1);
  align-items: center;
  display: flex;
  flex-direction: row;
  justify-content: center;
`;

const Circle = styled(motion.div)`
  background-color: #00a5ff;
  width: 50px;
  height: 50px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.1);
`;

function App() {
  const [clicked, setClicked] = useState(false);
  const toggleClicked = () => setClicked((prev) => !prev);
  return (
    <Wrapper onClick={toggleClicked}>
      <Box>
        {!clicked ? (
          <Circle layoutId="circle" style={{ borderRadius: 50 }} />
        ) : null}
      </Box>
      <Box>
        {clicked ? (
          <Circle layoutId="circle" style={{ borderRadius: 0 }} />
        ) : null}
      </Box>
    </Wrapper>
  );
}
export default App;




실행결과
오른쪽으로 가면 borderRadius가 0으로 바뀌면서
자동으로 animation이 적용됨을 볼 수 있다.




scale도 변화시켜보면

import { styled } from "styled-components";
import { motion } from "framer-motion";
import { useState } from "react";

// style
const Wrapper = styled(motion.div)`
  height: 100vh;
  width: 100vw;
  align-items: center;
  display: flex;
  flex-direction: row;
  justify-content: center;
  background: linear-gradient(135deg, rgb(252, 191, 248), rgb(21, 151, 203));
`;

const Box = styled(motion.div)`
  width: 300px;
  height: 300px;
  background-color: rgba(255, 255, 255, 1);
  border-radius: 40px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.1);
  align-items: center;
  display: flex;
  flex-direction: row;
  justify-content: center;
`;

const Circle = styled(motion.div)`
  background-color: #00a5ff;
  width: 50px;
  height: 50px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.1);
`;

function App() {
  const [clicked, setClicked] = useState(false);
  const toggleClicked = () => setClicked((prev) => !prev);
  return (
    <Wrapper onClick={toggleClicked}>
      <Box>
        {!clicked ? (
          <Circle layoutId="circle" style={{ borderRadius: 50 }} />
        ) : null}
      </Box>
      <Box>
        {clicked ? (
          <Circle layoutId="circle" style={{ borderRadius: 0, scale:3 }} />
        ) : null}
      </Box>
    </Wrapper>
  );
}
export default App;




실행결과

 

 

 

Comments