React, Next, Redux/⚛ React.JS

react-spring 소개 및 간단한 이용법

DarrenKwonDev 2021. 2. 2. 11:49

spring!

이번 프로젝트에서는 다루는 asset들이 패션 모델들의 이미지였기 때문에 프론트엔드에서 애니메이션, 트랜지션 등 화려한 뷰를 보여줄 필요가 있었다. 그래서 이번 기회에 react-spring을 써보기로 했다. 이번 아니면 언제 쓰겠는가.

www.react-spring.io/

 

react-spring

Bring your components to life with simple spring animation primitives for React

www.react-spring.io

자연스러운 애니메이션을 위해 스프링의 원리를 기반으로 한 애니메이션을 설계했다고 한다.

React, React-Native, React-Native-Web 에서 모두 사용 가능하다.

 

 

설치

yarn add react-spring

 

용량과 트리 쉐이킹

Everything included you end up with currently 10.7KB (web), 9.7KB (react-native) or 6.9KB (/universal export). The size will ultimately depend on your build-chain and can decrease with tree-shaking. For instance, just importing useSpring without color-interpolations (using the /universal export) you will get 4.7KB.

 

용량이 큰 것은 아니지만, 트리쉐이킹을 할 수 있으니해서 나쁠 것은 없습니다.

 

Basic

react-spring은 5개의 훅으로 이루어져있다. 이 훅 5개를 사용할 줄 알면 react-spring은 박.살.

각 훅에 공식 문서로 가는 경로를 달아놨으니 필요할 때 찾아가자.

 

  • useSpring : data를 a에서 b로 이동시키는 spring이다.
  • useSprings : multiple spring이다.
  • useTrail : 단일 dataset을 위한 multiple spring이다. 하나의 spring을 다른 것들이 따라간다. 그래서 "Trail"이다.
  • useTransition : 트랜지션의 mount/unmount을 위해 사용된다.
  • useChain : 여러 애니메이션을 중첩하여 적용하거나, 순차적으로 적용하기 위해 사용한다. 그래서 "Chain"이다.

공식 문서에서는 가장 간단한 useSpring을 통해서 spring에 대한 개념을 이해시키고 있는데 이걸 그대로 따라가보자.

 

괜히 verbose해져서 코드에 주석으로 다 달았다.

요약하자면, animated로 컴포넌트나 html 태그를 확장하고, useSpring으로 spring 애니메이션을 적용하면 된다.

 

* 여담으로, 공식문서에 있는 예시 코드가 잘못되었다. 사라지려면 1에서 시작해서 0으로 없어져야 한다. 아래 코드에선 고쳐놨다.

// animated는 react 외부에서 구현된 애니메이션 factory이다. react의 도구들, 예컨대 hook으로 애니메이션을 작성하면 성능이 좋지 않기 때문
import { useSpring, animated } from "react-spring";

function App() {
  // useSpring을 이용하여 spring을 정의함.
  // spring은 a에서 b로 움직이는 스프링 애니메이션.
  // self-updating을 하는 동적인 값이며(타입으론 AnimatedValue로 되어있음), static value가 아님
  const props = useSpring({ opacity: 0, from: { opacity: 1 } });

  // native tag를 extends 한 것으로, animated.tag 꼴로 사용해줌. ex - animated.div, animated.span, animated.svg ...
  // 만약 React Component를 animated하게 확장하고 싶다면 animated(App), animated(Login) 과 같이 단순히 ()로  감싸면 된다.
  // css-in-js인 styled-components나 emotion 등을 확장하려면 const AnimatedHeader = styled(animated.h1) 꼴로 사용하면 된다.
  return <animated.div style={props}>I will fade in</animated.div>;
}

export default App;

 

spring이 적용되는 것을 수치적으로 보고 싶다면 0부터 1까지 이동시켜 확인해보자.

import { useSpring, animated } from "react-spring";

function App() {
  const props = useSpring({ number: 1, from: { number: 0 } });
  return <animated.span>{props.number}</animated.span>;
}

export default App;

 

어쨌거나 아래와 같이 useSpring에 다양한 값을 전달하여 spring 애니메이션을 사용할 수 있다.

const props = useSpring({
  vector: [0, 10, 30],
  display: 'block',
  padding: 20,
  background: 'linear-gradient(to right, #009fff, #ec2f4b)',
  transform: 'translate3d(0px,0,0) scale(1) rotateX(0deg)',
  boxShadow: '0px 10px 20px 0px rgba(0,0,0,0.4)',
  borderBottom: '10px solid #2D3747',
  shape: 'M20,20 L20,380 L380,380 L380,20 L20,20 Z',
  textShadow: '0px 5px 15px rgba(255,255,255,0.5)'
})

 

 

- interpolation

 

interpolation('보간법')하면 점과 점 사이를 직선으로 연결하지 않고 부드럽게 곡선으로 연결하는, 근사적인 함수이 떠오른다. 그런데 react-spring의 내부적인 수학적 원리를 차치하고, 그냥 a에서 b로 이동하는 애니메이션의 값을 이용하여 변수처럼 사용할 수 있게 해주는 메서드라고 이해하는 게 쉽다.

 

왜, 모든 속성을 spring으로 작성할 수 없지는 않은가? border with를 위한 prop 하나 만들고, margin을 위한 prop 하나 만들고, 이렇게 특정 속성을 컨트롤하기 위해 prop를 계속 만드는 건 가독성이 좋지 않다.

 

이 녀석은 다른 걸 구현해보면서 직접 이해해보는게 직접 와닿는다.

import { useSpring, animated, interpolate } from "react-spring";

function App() {
  const { o, xyz, color } = useSpring({
    from: { o: 0, xyz: [0, 0, 0], color: "red" },
    o: 1,
    xyz: [10, 20, 5],
    color: "green",
  });

  return (
    <animated.div
      style={{
        color, // 일반 spring 애니메이션이 적용됨

        // 가장 기본적인 interpolate 사용
        background: o.interpolate((o) => `rgba(210, 57, 77, ${o})`),

        // array로 된 값을 각각 뿌리는데 interpolate를 사용함.
        // 참고로, useSpring을 통해 이동하는 값은 자체적으로 interpolate 메서드를 지니고 있음. 굳이 import 안해도 됨
        transform: xyz.interpolate(
          (x, y, z) => `translate3d(${x}px, ${y}px, ${z}px)`
        ),

        // 여러 spring prop을 섞어 쓰고 싶은 경우 아래처럼 하면 됨.
        border: interpolate([o, color], (o, c) => `${o * 10}px solid ${c}`),

        // interpolate는 체인처럼 사용할 수 있음. type 이름도 InterpolationChain임
        padding: o
          .interpolate({ range: [0, 0.5, 1], output: [0, 0, 10] })
          .interpolate((o) => `${o}%`),
      }}
    >
      {o.interpolate((n) => n.toFixed(2)) /* innerText interpolation ... */}
    </animated.div>
  );
}

export default App;