Framer Motion/개념정리

[FRAMER MOTION] Variants

xowoony 2023. 5. 11. 19:36

학습일 : 2023. 05. 11



Variants 을 사용하게 되면
1. 코드가 깔끔해짐
2. 많은 애니메이션들을 하나로 연결시켜줌

 


Variant란 비유하자면
내 애니메이션의 stage(무대)이다.
예를 들면, initial이 있을 수도 있고 showing, hidden, from, to, 0%, 100% 등등
내가 원하는 무엇이든 될 수 있다.

 


이 variant를 자바스크립트 오브젝트에 넣어 작성해주도록 한다.
이름은 자유롭게 지어주고 적용해주면 된다.

const myVars = {
  // 초기상태 - start라고 이름 지어 주겠음
  // 이전의 코드에서 initial 부분을 이쪽으로 옮겨주겠음.
  start : { scale: 0 },
 // 최종 상태 - end라고 이름 지어 주겠음
  // 이전의 코드에서 animate, transition 부분을 이쪽으로 옮겨주겠음.
  end : { scale: 1, rotateZ: 360,  }
}



다 옮겨주었으니
이전 코드에서 initial, animate, transition prop들은 지워주도록 한다.

이제 이 myVars라는 이름의 variants prop을 사용하기만 하면 된다.

 

 


사용하는 방법은 아래와 같다.

<Box variants={myVars} initial="start" animate="end"/>


또는

<Box variants={myVars} initial={myVars.start} animate={myVars.end}/>




실행결과
저번과 똑같은 결과가 정상적으로 나옴









이제 이런걸 만들어 보고자 하는데,

 

 


스타일링을 해주고

 


<App.tsx>

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

// style
const Wrapper = styled.div`
  height: 100vh;
  width: 100vw;
  display: flex;
  justify-content: center;
  align-items: center;
`;

// 박스
const Box = styled(motion.div)`
  width: 200px;
  height: 200px;
  background-color: rgba(255, 255, 255, 0.2);
  border-radius: 40px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.06);
  display: grid;
  grid-template-columns: repeat(2, 1fr);
`;

// 원
const Circle = styled(motion.div)`
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.06);
  background-color: white;
  height: 70px;
  width: 70px;
  border-radius: 35px;
  place-self: center; // 원들이 박스에서 center로 위치하게 됨
`;

function App() {
  return (
    <Wrapper>
      <Box>
        <Circle />
        <Circle />
        <Circle />
        <Circle />
      </Box>
    </Wrapper>
  );
}
export default App;




1. Box가 먼저 나타남
2. 원들이 나타남


이걸 위해 variants를 만들어주도록 하겠음.

const boxVariants = {
  start: {},
  end: {},
}


그리고 Box 컴포넌트에 prop전달

 <Box variants={boxVariants} initial="start" animate="end">





<App.tsx>
박스의 효과를 먼저 만들었다.

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

// style
const Wrapper = styled.div`
  height: 100vh;
  width: 100vw;
  display: flex;
  justify-content: center;
  align-items: center;
`;

// 박스
const Box = styled(motion.div)`
  width: 200px;
  height: 200px;
  background-color: rgba(255, 255, 255, 0.2);
  border-radius: 40px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.06);
  display: grid;
  grid-template-columns: repeat(2, 1fr);
`;

// 원
const Circle = styled(motion.div)`
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.06);
  background-color: white;
  height: 70px;
  width: 70px;
  border-radius: 35px;
  place-self: center; // 원들이 박스에서 center로 위치하게 됨
`;

// Variants
const boxVariants = {
  start: {
    opacity: 0,
    scale: 0.5,
  },
  end: {
    opacity: 1,
    scale: 1,
    transition: {
      type: "spring",
      duration: 2,
      bounce: 0.5,
    },
  },
};

function App() {
  return (
    <Wrapper>
      <Box variants={boxVariants} initial="start" animate="end">
        <Circle />
        <Circle />
        <Circle />
        <Circle />
      </Box>
    </Wrapper>
  );
}
export default App;



실행결과




다음은 원 4개의 애니메이션을 만들어볼 것이다.

Circle컴포넌트는
Box의 자식 컴포넌트 이기 때문에
기본값으로 Motion이 initial과 animate를 준다.

따라서

<App.tsx>
circleVariants에도 이름을 start, end로 Box와 똑같이 부여해주었다.
기본적으로 motion은 부모 컴포넌트의 것을 자식들 각각에 복붙해주기 때문이다.


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

// style
const Wrapper = styled.div`
  height: 100vh;
  width: 100vw;
  display: flex;
  justify-content: center;
  align-items: center;
`;

// 박스
const Box = styled(motion.div)`
  width: 200px;
  height: 200px;
  background-color: rgba(255, 255, 255, 0.2);
  border-radius: 40px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.06);
  display: grid;
  grid-template-columns: repeat(2, 1fr);
`;

// 원
const Circle = styled(motion.div)`
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.06);
  background-color: white;
  height: 70px;
  width: 70px;
  border-radius: 35px;
  place-self: center; // 원들이 박스에서 center로 위치하게 됨
`;

// Variants
const boxVariants = {
  start: {
    opacity: 0,
    scale: 0.5,
  },
  end: {
    opacity: 1,
    scale: 1,
    transition: {
      type: "spring",
      duration: 2,
      bounce: 0.5,
    },
  },
};

const circleVariants = {
  start: {
    scale: 0,
  },
  end: {
    scale: 2,
    transition: {
      type: "spring",
      bounce: 0.8,
    },
  },
};

function App() {
  return (
    <Wrapper>
      <Box variants={boxVariants} initial="start" animate="end">
        <Circle variants={circleVariants} />
        <Circle variants={circleVariants} />
        <Circle variants={circleVariants} />
        <Circle variants={circleVariants} />
      </Box>
    </Wrapper>
  );
}
export default App;



따라서 이제 이 Box와 Circle은 
둘 다 동일한 initial인 start, animate인 end를 가지게 된다.
start, end란 이름을 같게 해주었기 때문이다.

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

// style
const Wrapper = styled.div`
  height: 100vh;
  width: 100vw;
  display: flex;
  justify-content: center;
  align-items: center;
`;

// 박스
const Box = styled(motion.div)`
  width: 200px;
  height: 200px;
  background-color: rgba(255, 255, 255, 0.2);
  border-radius: 40px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.06);
  display: grid;
  grid-template-columns: repeat(2, 1fr);
`;

// 원
const Circle = styled(motion.div)`
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.06);
  background-color: white;
  height: 70px;
  width: 70px;
  border-radius: 35px;
  place-self: center; // 원들이 박스에서 center로 위치하게 됨
`;

// Variants
const boxVariants = {
  start: {
    opacity: 0,
    scale: 0.5,
  },
  end: {
    opacity: 1,
    scale: 1,
    transition: {
      type: "spring",
      duration: 2, // box가 마무리 될때까지 2초 걸림
      bounce: 0.5,
      // delayChildren: 0.5, // 모든 자식 통째로 0.5초의 delay주기
      staggerChildren: 0.2, // 자식1, 자식2, 자식3, 자식4가 차례로 0.2초 간격으로 나옴
    },
  },
};

const circleVariants = {
  start: {
    opacity: 0,
    y: 10, 
  },
  end: {
    opacity: 1,
    y: 0, // 밑에서부터 위로 올라옴
  },
};

function App() {
  return (
    <Wrapper>
      <Box variants={boxVariants} initial="start" animate="end">
        <Circle variants={circleVariants} />
        <Circle variants={circleVariants} />
        <Circle variants={circleVariants} />
        <Circle variants={circleVariants} />
      </Box>
    </Wrapper>
  );
}
export default App;




실행결과