Framer Motion 애니메이션 라이브러리

2023. 3. 6. 12:27·TECH
목차
  1. 설치
  2.  
  3. 기본 props
  4. Variant
  5. Gesture
  6. motionValue
  7. SVG
  8. animatePresence
  9. animatePresence 로 Slider구현
  10. custom 
  11. layout
  12. layoutId 
  13. useAnimation
  14. animate keyframe

애니메이션 라이브러리

Framer Motion

demo

api 

 

설치

npm i framer-motion

 

 

div에 animation을 적용하고 싶을 때 motion에 있는 html태그를 사용해줘야한다. 

import { motion } from "framer-motion";

export default function App() {
  return (
    <div className="App">
      <motion.div></motion.div>
    </div>
  );
}

 

 

styled-components에서 motion을 사용해야하는 경우

// 타입선언은 괄호 뒤에 한다.
const Box = styled(motion.div)<{ bg: string}>`
	....
`;

 

background-color를 변경해야하는 경우 red, blue가 아닌 rgba 컬러를 사용해야한다. 

 

기본 props

initial - 애니메이션 적용하기 전 초기스타일

animate - 애니메이션 적용 스타일 

transition- 트랜지션 속성

<Box transition={{ delay: 3 }} initial={{ borderRadius: "10px"}} animate={{ borderRadius: "100px" }} />

 

transform-origin - transition을 사용할 때 어느위치에서 애니메이션을 할지 지정할 수도있다. (기본 css 속성) 

transform-origin: right center;

 

Variant

variant를 사용하면 코드 정리가 된다.

animation에 관련된 props속성을 분리 된 Object로 옮긴 것.

오브젝트이름, 오브젝트 내 property 이름은 제약없다. 

적용할 컴포넌트에 variants props로 object이름 넣어준다.
필요한 props에 Object property 이름 넣어준다. 

const myVars = {
  start: { scale: 0 },
  end: { scale: 1, rotateZ: 360, transition: { type: "spring", delay: 0.5 } },
};

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

 

부모 컴포넌트에 variants가 적용되어있으면 props 복사해서 적용해준다. 
자식 컴포넌트에 따로 적용하지 않아도 된다. 

아래 코드에서 Circle 컴포넌트에 initial="start" animate="end"가 자동으로 들어가있다. 

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

 


자식에서 다른 animation을 적용하려는 경우 
자식 Variants Object를 만들고 property 이름을 부모와 동일하게 해주면

자식 컴포넌트에서 variants props만 넘겨주면 된다. 

const boxVars = {
  start: { },
  end: { },
};

const circleVars = {
  start: { },
  end: { },
};

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

 

 

부모 애니메이션 속성에서 자식 애니메이션을 컨트롤 할 수 있다. 

delayChildren - 자식 delay

staggerChildren - 자식들 간의 delay

const boxVars = {
  start: { scale: 0 },
  end: {
    scale: 1,
    transition: { 
      type: "spring", 
      bounce: 0.25, 
      delayChildren: 0.5, // 자식 delay
      staggerChildren: 0.2  // 자식들의 delay
    },
  },
};

const circleVars = {
  start: { opacity: 0, y: 10 },
  end: { opacity: 1, y: 0, transition: { type: "spring" } },
};

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

 

Gesture

whileHover, whileTap

마우스 호버, 클릭 시 등등 다양한 제스처에 대한 애니메이션

const boxVars = {
  hover: { scale: 1.5, rotate: 180 },
  click: { borderRadius: "100px" },
};

function App() {
  return (
    <Wrapper>
      // whileHover: 마우스 호버, whileTap: 클릭
      <Box variants={boxVars} whileHover="hover" whileTap="click"></Box>
    </Wrapper>
  );
}

 

drag

드래그 애니메이션

드래그 시키고 싶은경우 drag props를 넣어주면 된다. (간편-)

// drag="x" x좌표로만 드래그가능
// drag="y" y좌표로만 드래그가능
// whilDrag 드래그할 때 적용할 애니메이션

<Box drag="x" variants={boxVars} whileDrag="drag" ></Box>

 

drag constraint (드래그 가능한 영역 제한)

1. 수치를 넣어서 영역을 제한하는 방법

<Box
  drag
  variants={boxVars}
  dragConstraints={{ top: -50, bottom: 50, left: -50, right: 50 }}
  whileDrag="drag"
></Box>

 

2. dom으로 영역을 제한하는 방법

function App() {
  const boxRef = useRef<HTMLDivElement>(null);

  return (
    <Wrapper>
      <BoxWapper ref={boxRef}>
        <Box
          drag
          variants={boxVars}
          dragConstraints={boxRef}
          whileDrag="drag"
        ></Box>
      </BoxWapper>
    </Wrapper>
  );
}

 

 

어디로 드래그 해도 제자리로 돌아오게 하는 법

// 1. 수치로 넣어주는 방법
dragConstraints={{ top: 0, bottom: 0, left: 0, right: 0 }}

// 2. 간단한 props로 적용하는 방법
dragSnapToOrigin

 

 

드래그 할때 잡아끄는 힘 조정하기 

dragElastic = {1} 잡아당기는 힘 0-1까지

 

motionValue

애니메이션 했을 때 계산되는 값 

motionValue가 업데이트 될 때 react rendering cycle을 발동시키지 않는다. 

그래서 컴포넌트는 다시 렌더링 되지 않는다. 

 

useMotionValue

function App() {
  const x = useMotionValue(0);
  console.log(x); // x가 변경되도 console로그는 로드할때 한번밖에 찍히지 않는다. 

  return (
    <Wrapper>
      <Box
        style={{ x: x }} // 키와 value가 같아서 x하나만 써줘도 된다. 
        drag="x"
        variants={boxVars}
        whileDrag="drag"
      ></Box>
    </Wrapper>
  );
}

 

값을 출력하고 싶은경우 useEffect를 사용해서 확인한다. 

useEffect(() => {
  x.onChange(() => {
  	console.log(x.get());
  });
}, [x]);

 

useTransform

motionValue값에 따른 애니메이션을 적용할 때 사용

예를 들어 

x값이 -800인 경우 scale값을 2로 

x값이 0인 경우 scale값을 1로

x값이 800인 경우 scale값을 0로 

useTransform(x, [-800, 0, 800], [2, 1, 0])// 어떤값, 값의 위치, output
// 값의 위치, output 길이 같아야한다
function App() {
  const x = useMotionValue(0);
  const scale = useTransform(x, [-400, 0, 400], [2, 1, 0]);

  return (
    <Wrapper>
      <Box
        style={{ x, scale }}
        drag="x"
        variants={boxVars}
        whileDrag="drag"
      ></Box>
    </Wrapper>
  );
}

 

useScroll

scrollY - 스크롤 값

scrollProgress 스크롤값에 따라 달라지는 값 , 0-1사이의 값

 

SVG

fill, pathLength로 애니메이션을 적용할 수 있다. 

const SvgWrapper = styled.svg`
  width: 200px;
  height: 200px;

  path {
    stroke-width: 5;
    stroke: white;
  }
`;

function App() {
  const svgVar = {
    start: { pathLength: 0, fill: "rgba(255, 255, 255, 0)" },
    end: { pathLength: 1, fill: "rgba(255, 255, 255, 1)", transition: { duration: 3 } },
  };

  return (
    <Wrapper>
      <SvgWrapper xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
        <motion.path
          variants={svgVar}
          initial="start"
          animate="end"
          d="..."
        />
      </SvgWrapper>
    </Wrapper>
  );
}

 

 

fill, pathLength 애니메이션을 따로 transition을 주고싶은경우 transition props를 써주고
모든 요소에 들어가는 건 default로 써주면 되고 
특정요소에 들어가는건 property 이름 써주고 하면 된다.

<Wrapper>
  <SvgWrapper xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
    <motion.path
      variants={svgVar}
      initial="start"
      animate="end"
      transition={{
        default: { duration: 5 }, // 모든요소에 적용
        fill: { duration: 3, delay: 2 }, // fill요소에만 적용
      }}
      d="..."
      />
  </SvgWrapper>
</Wrapper>

 

animatePresence

나타나고, 사라지는 애니메이션 적용 

animatePresence 컴포넌트 내부에는 display 조건문이 있어야한다. 

조건문안에 animationPresence 컴포넌트가 위치하면 안된다.

 

exit props - element가 사라질때 적용하는 애니메이션 

function App() {
  const [isVisible, setVisible] = useState(false);
  const toggleClick = () => {
    setVisible((prev) => !prev);
  };

  const BoxVars = {
    initial: { scale: 0, opacity: 0 },
    visible: { opacity: 1, scale: 1 },
    exit: { y: 50 },
  };

  return (
    <Wrapper>
      <AnimatePresence>
        {isVisible ? (
          <Box variants={BoxVars} initial="initial" animate="visible" exit="exit" />
        ) : null}
      </AnimatePresence>
      <div>
        <button onClick={toggleClick}>click me</button>
      </div>
    </Wrapper>
  );
}

 

onExitComplete - exit가 끝나고 실행되는 것 

initial - 초기에 애니메이션을 적용할 건지 ( true로 하면 로드될 때 애니메이션이 동작한다. )

<AnimatePresence onExitComplete={toggleLeaving} initial={false}>

 

 

 

animatePresence 로 Slider구현

function App() {
  const [currentPage, setCurrentPage] = useState(1);

  const clickPrev = () => {
    setCurrentPage((current) => (current === 1 ? 1 : current - 1));
  };
  const clickNext = () => {
    setCurrentPage((current) => (current === 10 ? 10 : current + 1));
  };

  const BoxVars = {
    initial: { x: 500, scale: 0, opacity: 0 },
    visible: { x: 0, opacity: 1, scale: 1 },
    exit: { x: -500, scale: 0 },
  };

  return (
    <Wrapper>
      <AnimatePresence>
        {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((num) =>
          currentPage === num ? (
            <Box variants={BoxVars} initial="initial" animate="visible" exit="exit" key={num}>
              {num}
            </Box>
          ) : null
        )}
      </AnimatePresence>

      <div style={{ position: "fixed", bottom: 200 }}>
        <button onClick={clickPrev}>prev</button>
        <button onClick={clickNext}>next</button>
      </div>
    </Wrapper>
  );
}

 

custom 

variants에 데이터를 보낼수있게 해주는 property

// variants에서 매개변수로 받아서 분기처리 해줄수있다.
const BoxVars = {
    initial: (direction: boolean) => ({ x: direction ? -500 : 500, scale: 0, opacity: 0 }),
    visible: { x: 0, opacity: 1, scale: 1, transition: { duration: 1 } },
    exit: (direction: boolean) => ({
      x: direction ? 500 : -500,
      scale: 0,
      transition: { duration: 0.5 },
    }),
};

// AnimatePresence와 해당컴포넌트에 custom props를 넣어준다.
<AnimatePresence custom={direction}>
  <Box
    custom={direction}
    variants={BoxVars}
    initial="initial"
    animate="visible"
    exit="exit"
    key={currentPage}
  >
  	{currentPage}
  </Box>
</AnimatePresence>

 

next 버튼을 누르면 1카드가 exit 되는 animation과 2카드 나타나는 animation이 동시에 일어난다.
mode="wait" 를 사용하면 1카드가 exit된 다음 2카드 나타나는 애니메이션이 일어난다.

<AnimatePresence  mode="wait" >
</AnimatePresence>

 

layout

부모요소가 조건에 따라 속성이 변경되서 애니메이션 되어야할때 layout 속성을 준다.
framer motion은 무언가 외부의 힘에 의해 바뀐것을 감지한다.

<Box
  style={{
  alignItems: isClick ? "center" : "flex-start",
  justifyContent: isClick ? "center" : "flex-start",
  }}
>
	<Circle layout />
</Box>

layout 적용 전
layout 적용 후

layoutId 

다른 컴포넌트지만 framer motion에게 같은 컴포넌트라고 지정해서 애니메이션을 줄 수 있다. 

<Box>{isClick ? <Circle layoutId="circle1" /> : null}</Box>
<Box>{!isClick ? <Circle layoutId="circle1" /> : null}</Box>

layoutId 적용 전
layoutId 적용 후

 

useAnimation

다른역활을 하는 함수안에서 애니메이션을 제어하고 싶은경우 사용한다. 
(ex 토글하는 클릭 이벤트에서 애니메이션을 제어하는 경우)

inputAnimation = useAnimation();
  // 클릭했을때 state active시키는 코드에서 애니메이션을 실행하고 있다.
  const toggleSearchActive = () => {
    if (searchActive) {
      inputAnimation.start({
        scaleX: 0,
      });
    } else {
      inputAnimation.start({
        scaleX: 1,
      });
    }
    setSearchActive((prev) => !prev);
  };
  
  return (
  	<Input animate={inputAnimation}/>  
  )
}

 

useAnimation을 사용할 때에도 variants를 사용할 수 있다. 

const navVariants = {
  top: {
    backgroundColor: "rgba(0, 0, 0, 1)",
  },
  scroll: {
    backgroundColor: "rgba(0, 0, 0, 0)",
  },
};

function Header() {
  const navAnimation = useAnimation();
  const { scrollY } = useScroll();
  
  useEffect(() => {
    scrollY.onChange(() => {
      if (scrollY.get() > 80) {
        navAnimation.start("scroll"); // variants의 property이름
      } else {
        navAnimation.start("top");
      }
    });
  }, [scrollY, navAnimation]);
  
  return (
  	<Nav variants={navVariants} animate={navAnimation}>
  )
}

 

간단한 animation 변경은 useAnimation hook을 이용하는것보다

animate props를 수정하는게 더 효율적이다. 
 

animate keyframe

const logoVariants = {
  normal: {
    fillOpacity: 1,
  },
  active: {
    fillOpacity: [0, 1, 0], // 0, 50%, 100%
    transition: {
      repeat: Infinity,
    },
  },
};

 

 

'TECH' 카테고리의 다른 글

React Hooks  (0) 2023.03.08
React 기본개념  (0) 2023.03.08
react-router-dom v6  (0) 2023.03.02
Recoil 개념  (0) 2023.03.02
React Hook Form  (0) 2023.03.01
  1. 설치
  2.  
  3. 기본 props
  4. Variant
  5. Gesture
  6. motionValue
  7. SVG
  8. animatePresence
  9. animatePresence 로 Slider구현
  10. custom 
  11. layout
  12. layoutId 
  13. useAnimation
  14. animate keyframe
'TECH' 카테고리의 다른 글
  • React Hooks
  • React 기본개념
  • react-router-dom v6
  • Recoil 개념
ssund
ssund
  • ssund
    ssund의 기술블로그
    ssund
  • 전체
    오늘
    어제
    • 분류 전체보기 (73)
      • TECH (22)
      • NOTE (40)
      • DAILY (7)
      • javascript (1)
      • 알고리즘 (0)
  • 블로그 메뉴

    • 홈
    • TECH
    • NOTE
    • DAILY
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    JavaScript
    theme-provider
    redux
    함수와 메서드차이
    d.ts
    react state management
    TypeScript
    타입스크립트
    reat-head
    git배포
    웹브라우저구성
    reduxtoolkit
    커머스프로젝트
    React
    Array.sort()
    styled-components
    call signatures
    slidesPerGroup
    global-style
    배열요소순서
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
ssund
Framer Motion 애니메이션 라이브러리

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.