dukjjang

Framer motion으로 애니메이션 효과 주기

Framer motion를 사용하여 애니메이션을 주는 방법설명과 적용예제 (useSpring,dragTransition)

dukjjang

dukjjang

2023년 2월 8일

css

이번 내 웹사이트를 작업하면서 Framer motion을 아주 유용하게 사용하였다. Framer motion은 리액트를 위한 애니메이션 라이브러리로서 심플하고 간결한 구문으로 애니메이션을 쉽게 컨트롤 할 수 있도록 도와준다. 오늘은 Framer motion의 간단한 사용법과 더불어 내가 어떻게 애니메이션 작업을 했는지에 대해 정리해보려 한다.

기존의 Animation 적용

보통 애니메이션을 작업할때는 따로 keyframes을 정의하고 만든 keyframe을 원하는 컴포넌트의 클래스에 적용시킨다.

nextButton {
  background-color: pink;
  padding: 5px;
  animation: arrowRight 4s 1s infinite linear;
}

@keyframes arrowRight {
  0% {
    transofrom: translateX(0px);
  }
  50% {
    transofrom: translateX(20px);
  }
  100% {
    transofrom: translateX(0px);
  }
}

 <button className="nextArrow">next</button>

이는 최신 라이브러리인 tailwindcss도 마찬가지다. tailwind.config.css 에서 keyfream을 만들어준 뒤에 컴포넌트의 className에서 적용하는 방식이다.

막상 애니메이션에 대한 아이디어가 떠올라도 구현하려면 귀찮아질 때도 있다.

하지만 ramer motion을 사용하면 간단하게 해결 할 수 있다.

Framer motion 설치, 적용

framer-motion 설치

yarn add framer-motion

애니메이션을 적용시킬 요소의 태그 앞에 motion을 붙혀주면 준비는 끝이다. 그리고 나서 animate 속성을 사용하여 애니메이션을 지정해 주면 된다.

import {motion} from 'framer-motion'


<motion.button animate={{...}} />

keyframes

keyframes를 따로 정의할 필요없이 배열로 만들어서 표현할 수 있다. 배열의 첫번째 요소가 0% 두번째가 50% 세번째가 100%가 된다. (duration 값에 따라서 퍼센트는 달라진다)

<motion.button
      animate={{ transLateX: [0,5,0] }}
      transition={{
        repeat: Infinity,
        duration: 3,
      }}
      }
    >next</motion.button>

구현된 모습

Blog Post Image

initial(초기값)

또한 컴포넌트가 첫 랜더링 될때의 초기값을 initial 속성을 통해 제어 할 수도 있다.

<motion.div initial={{...}} />

초기값을 사용하면 첫 랜더링시 시작 위치를 다르게 한다거나, 처음엔 opacity 0에서 랜더링 되면 서서히 opacity가 100이 되게끔 연출하는 등의 상황에서 유용하게 쓰인다.

나는 유저가 화면에 진입했을 때 텍스트가 위에서 아래로 떨어지는 효과를 주기위해서 initial 값을 -y30으로 화면 바깥에서 시작하게 만들어주었다. 여기에 더해 자연스러운 연출을 위해 opacity값도 초기값 0에서 1로 주었다.

<motion.div
      initial={{ opacity: 0, translateY: -30 }}
      animate={{ opacity: 1, translateY: 0 }}
      transition={{ duration: 0.7 }}
    >
      {children}
    </motion.div>

Blog Post Image

useSpring

useSpring은 스프링 애니메이션을 커스텀할 수 있는 hook이다. 이 훅은 spring motion value를 생성한다. 이를 원하는 컴포넌트에 적용할 수 있다.

먼저 useSpring hook을 임포트해준다.

import { useSpring } from "framer-motion";

그다음 x라는 상수를 만들고 spring value를 만들어준다.

const x = useSpring(0)

여기에 기본 spring type transition과 같이 옵션값을 넣어줄 수 있다.

const x = useSpring(0,{ stiffness: 300, damping: 5 })

stiffness는 강성도 수치이다. 이 수치가 높으면 원래대로 돌아오려는 강도가 높아져 스프링 효과가 높아진다.

damping는 진동의 크기를 줄이는 비율이다. 비율이 높으면 진동은 줄어들고, 스프링 효과가 줄어든다.

이제 애니메이션을 줄 div를 만들고, x위치를 이동시키는 애니메이션을 주고, inline style로 x값에 위에서 만든 spring x값을 넣어보자

<motion.div
          animate={{ x: [500, 0] }}
          style={{
            x,
            width: 100,
            height: 100,
            backgroundColor: "pink",
            borderRadius: "50%",
          }}
        />

그러면 x:0 위치로 돌아왔을때 재밌는 스프링 효과가 일어나는 것을 볼 수 있다.

spring 적용 X

Blog Post Image

spring 적용

Blog Post Image

Drag Transition

만약 드래그앤드롭 상황에서 스프링 효과를 주고 싶다면 drag transition으로 구현 할 수 있다.

먼저 트랜지션을 적용시킬 모션 컴포넌트에 drag 속성을 넣어준다.

drag: 드래그 가능 여부 속성

drag속성이 부여된 요소는 마우스 드래그로 이리저리 옮겨질 수 있는 상태가 된다.

기본적으로 false이고 true값을 주면 x,y 모두 드래그 가능요소가 되고, 원하면 x값,y값만 줄 수도 있다. 만약 drag="x" 를 주었다면 좌우로만 드래그 가능하다.

모션 컴포넌트의 drag는 일반적인 draggable 속성과 달리 컴포넌트 요소 자체가 움직인다. 드래그 하는 마우스 좌표값에 따라서 컴포넌트의 위치값이 변한다.

        <motion.div
          drag
          style={{
            width: "100px",
            height: "100px",
            backgroundColor: "green",
            borderRadius: "50%",
          }}
        />

그랬더니 화면밖으로 나가버리는 현상이 나타났다. 이럴땐 dragConstraints 속성으로 드래그가 가능한 지역 범위를 제한 할 수 있다.

dragConstraints: 드래그 가능한 영역을 제한할 수 있는 속성

 <motion.div
          drag
          dragConstraints={{ top: 0, bottom: 100, left: 0, right: 300 }}
          style={{
            width: 100,
            height: 100,
            backgroundColor: "tomato",
            borderRadius: "50%",
          }}
        />

여기에 dragTransition과 elastic 속성을 사용하면 스프링 효과를 낼 수 있게 된다.

dragTransition: 드래그 transition을 설정할 수 있는 속성

  • bounceStiffness: useSpring의 Stiffness 속성과 같음
  • bounceDamping: useSpring의 damping 속성과 같음

dragElastic: 탄성도를 조절할 수 있는 속성

<motion.div
          drag
          dragConstraints={{ top: 0, bottom: 300, left: 0, right: 300 }}
          dragTransition={{ bounceStiffness: 300, bounceDamping: 7 }}
          dragElastic={2}
          style={{
            width: 100,
            height: 100,
            backgroundColor: "tomato",
            borderRadius: "50%",
          }}
        />

내가 적용한 예제

나는 이 사이트의 헤더 부분을 작업하면서 해와 달을 만들었는데, 유저가 사이트와 상호작용 할 수 있는 재미요소를 주고 싶었다. 그래서 드래그 트랜지션을 이용하여 해,달을 이리 저리 당겼다 놓을 수 있는 연출을 하였다.

지금까지 정리한 방법들을 사용하여 드래그 이동 범위를 0으로 제한하고, transition과 dragElastic을 적절하게 주어, 드래그 했다가 놓으면 다시 원래 위치로 튕겨져 돌아가는 재밌는 스프링 효과를 만들 수 있었다.

Blog Post Image

정리

이제는 웹이 단순히 글만 보고 정보만 얻는 공간에서 점점 유저와 상호작용 할 수 있는 공간으로 점점 발전하고 있다. 블로그는 정적인 컨텐츠를 소비하는 공간이지만, 이렇게 이리저리 클릭해보고 만져보고 움직여볼 수 있는 요소가 곳곳에 배치된다면 좀더 즐거운 유저 경험을 줄 수 있다고 생각한다.

분야마다 다르겠지만, 개인적으로는 렉걸리는 webGl 3d 애니메이션 들어간 웹사이트 보다, 깔끔하고 간결한 애니메이션이 가미된 웹에서 훨씬 세련되고 좋은 인상을 받았던 것 같다. 결국 좋은 컨텐츠 혹은 서비스가 우선이지만, 그 컨텐츠와 서비스를 잘 즐기고 소비할 수 있도록 연결하는 것이 프론트엔드 개발자의 역할이라고 생각한다. 제공하는 서비스의 본질을 해치지 않는 적절한 선에서, 적재적소에 애니메이션과 트랜지션을 주어서 좋은 유저경험을 주는것이 매우 중요하다는 생각이 든다. 나는 그러한 좋은 경험을 줄 수 있는 실력을 갖춘 개발자가 되는 것이 목표이다.

아무튼 Framer motion을 사용한 애니메이션 프로그래밍은 매우 좋은 개발 경험이였다. 깔끔하고 직관적이며 다양한 hook과 custom이 제공되어 편리했다.

아쉬웠던 점은 공식문서에 상세한 부분들은 기술 되어 있지 않아서 조금 불편했다. framer book이라는 사이트에는 모든 기능과 설명이 기술되어 있는데, 공식사이트가 아니고 유료이다. 하지만 그래서 왠지 더 끌린다. 뭔가 더 깊이 파고싶게끔 만드는 구석이 있다.🧓🏻 Framer 앞으로도 자주 애용하게 될 것 같다.