Spring Animation은 무엇인가 (개념 설명)
Spring 애니메이션은 물리세계를 반영한 애니메이션을 말한다. 애니메이션이라는 단어를 떠올리면 ease-in
또는 ease-out
같은 Bezier Curve 애니메이션이 생각나는데 이 애니메이션과는 무엇이 다를까?
효과적인 비교를 위해서 애니메이션을 사용하는 이유와 Bezier Curve
애니메이션에 대해서 간단하게 알아보자.
거의 모든 프로덕트에는 애니메이션이 포함되어 있다. 예를 들어 스위치의 on, off 상태가 변할 때 부드러운 스타일 전환을 위해서 애니메이션을 사용한다.
왜 부드러운 스타일 전환을 사용할까? 위치가 순식간에 변하는 것보다 변화하는 과정을 보여주는 것이 더 자연스럽게 느껴지기 때문이다.
위치뿐만 아니라 속도(velocity
)도 순식간에 변하는 것보다 서서히 변하는 게 더 자연스럽다.
왜냐하면 실제 물리 세계에서는 운동 중에 동일한 속도를 가지는 물체는 거의 없기 때문이다.(가속도가 없는 물체는 없다.)
그래서 CSS
속성인 transition-timing-function
에서 linear
보다는 속도가 서서히 변하는 ease-in
, ease-out
을 더 자주 사용한다.
ease-in
과 ease-out
를 사용한 애니메이션은 Bezier Curve
애니메이션에 속한다. Bezier Curve
애니메이션은 duration
과 curve
의 조합으로 구성된다.
Bezier curve
애니메이션은 x축이 시간이고 y축이 위치인 곡선 그래프로 표현할 수 있다. Bezier curve
의 구성요소 중 하나인 curve
는 그래프의 모양을 뜻한다. ease-in
, ease-in-out
는 curve
의 종류 중 하나다. duration
은 애니메이션이 끝날 때까지 걸리는 시간을 의미한다.
아래 이미지에서 볼 수 있듯, linear
는 일직선으로 이루어져 있어서 속도가 항상 일정하지만 ease-in
이나 ease-in-out
은 곡선으로 이루어져 있어서 속도가 연속적으로 변해서 훨씬 자연스럽게 느껴진다.
Spring
애니메이션도 Bezier curve
애니메이션과 비슷한 모양(곡선)을 가지지만 ease-in
, ease-in-out
처럼 정확한 모양을 가지지 않는다.
velocity(초기 속도)
, mass(질량)
, stiffness(탄성)
, damping(감쇠)
에 따라서 그 모양이 다르다. 이 점이 가장 큰 차이다. ease-in
,ease-in-out
과의 공통점은 속도와 위치가 연속적으로(서서히) 변한다는 것이다.
Spring Animation은 언제 사용할까?
Spring
애니메이션은 언제 사용해야 하는지 궁금할 것이다. 지금까지는 Bezier curve
와 다를 것이 없어 보인다. 사용자와의 인터렉션이 있을 때 Spring
애니메이션은 빛을 발한다.
핸들을 통해서 열림/닫힘을 조절할 수 있는 바텀시트를 생각해보자. 우리가 모바일에서 경험하는 바텀시트는 다음의 특징을 가진다. 바텀시트의 핸들을 잡고 빠르게 밀면 닫을 수 있지만 비교적 약하게 밀었을 때는 닫을 수 없다.
그리고 빠르게 밀수록 더 빠르게 바텀시트가 닫혀야한다. Bezier curve
애니메이션을 통해서는 빠르게 밀수록 바텀시트가 빠르게 닫히는 것을 자연스럽게 구현하기 힘들다.
사용자가 핸들을 민 속도에 따라서 애니메이션의 속도도 결정되어야 하지만 Bezier curve
의 초기 속도는 항상 정해져 있기 때문이다. 하지만 앞서 말한 네 가지 요소 (속도,질량,탄성,감쇠)에 의해 결정되는 Spring
애니메이션은 Spring
애니메이션을 통해서 더 자연스럽고 정교한 애니메이션을 만들 수 있다.
또 다른 예시로는 스크롤이 있다. 웹페이지를 스크롤 해보면 바로 멈추지 않고 서서히 멈춘다. 힘의 크기가 클수록 더 많이 내려간다.
Spring
애니메이션이라서 항상 용수철처럼 튕김(bounce)이 있어야 될 것 같지만 튕김이 있어야 꼭 Spring
애니메이션은 아니다.
앞서 소개했던 바텀시트와 스크롤 모두 튕김이 없는 Spring
애니메이션을 사용했다. 물리적 세계를 구현한 애니메이션이라고 생각하자.
Spring 애니메이션은 어떻게 동작하는가?
스프링 애니메이션은 네 가지 요소에 의해서 결정된다. 많은 Spring
애니메이션에서 옵션으로 사용되기에 알아두면 애니메이션 라이브러리를 사용할 때 유용하다.
탄성
(stiffness 혹은 tension)질량
(mass)초기 속도
(initial velocity)감쇠
(damping)
탄성과 질량
이제부터 용수철과 관련된 공식을 통해서 세 가지 요소에 대해서 설명해 보겠다. 물리를 몰라도 알 수 있다. 용수철을 생각해 보자. 용수철은 늘리거나 줄이면 다시 원래의 상태로 돌아가려고 한다. 그리고 늘리는 정도가 클수록 원래 상태로 돌아가려는 힘도 커진다. 공식을 써보면 다음과 같다.
k
는 탄성(stiffness)를 가리키는 상수다. k
값이 큰 봄은 더 강한 힘으로 원래 상태로 돌아가려고 하며, k 값이 작은 봄은 그만큼 약한 힘으로 돌아가려고 한다.
x는 변형 정도(늘어나거나 압축된 정도)를 뜻한다.
이 공식의 핵심은 이 법칙의 핵심은 작용하는 힘(F)이 그 변형 정도에 비례한다는 것이다.
이를 훅의 법칙이라고 한다.
그런데 F
를 보면 학교에서 한 번쯤 들었던 공식이 생각날 것이다. 바로 F = ma
다. 힘은 질량(mass
)과 가속도에 비례 한다는 것이다.
그렇다면 훅의 법칙을 다음과 같이 변형할 수 있다.
또 아래와 같이 표현할 수 있다. 이 공식을 통해서 질량(mass)이 작을수록 가속도가 커지는 것을 알 수 있다.
초기속도
초기속도는 어떤 상관이 있을까? 초기속도는 힘과 관련이 있다. 용수철을 세게 잡아당길수록 초기 속도는 빨라진다.
용수철이 원래 상태로 돌아가려고 할 때 더 빠른 속도로 움직인다는 것이다.
초기 속도(initial velocity
)는 물체에 가하는 힘이라고 생각하면 된다. 예를 들어 바텀시트 핸들을 얼마나 빨리 내리느냐 같은 것이다.
감쇠
훅의 법칙으로 현실 세계를 모두 표현할 수 없다. 현실 세계에서는 마찰이 존재한다. 하지만 훅의 법칙은 이를 고려하지 않았다. 실제 세계에서는 속도가 점차 감소해서 용수철이 결국 멈출 것이다. 이를 감쇠(damping)라고한다. 감쇠가 크면 클수록 더 빠르게 용수철이 멈춘다.
식으로 간단하게 표현하면 다음과 같다. 전체 힘을 용수철의 힘 Fs와 감쇠 힘 Fd로 나눌 수 있다.
Spring 애니메이션의 종류
Spring
애니메이션은 앞서 설명한 여러 요소들에 의해서 모양이 달라지지만 크게 세 가지로 나뉠 수 있다.
- Underdamped (미계쇠 상태)
이 상태에서 감쇠가 있지만 충분하지 않아 과하게 진동한다. 여러 번 진동하다가 멈추게 된다.
- Critically damped (임계 감쇠 상태)
임계 감쇠 상태에서는 가능한 가장 빠른 시간 내에 원래 위치로 돌아간다. 불필요한 진동 없이 정지한다. 이는 감쇠가 정확히 적절한 수준으로 설정되어 있을 때 발생한다.
- Overdamped (과감쇠 상태)
이 상태에서는 감쇠는 강해서 시스템이 원래 위치로 돌아오는 것이 느리다. 불필요한 진동이 없다.
각 상태는 여러 요소들의 조합으로 이루어진다. react-spring
이나 swift
에서는 bounce
라는 옵션 하나로 여러 요소들의 조합을 설정할 수 있게 간소화 하기도 했다.
Spring 애니메이션을 사용하면 안되는 경우
모든 애니메이션에 Spring
애니메이션을 사용해야 하는 것은 아니다. 예를 들어 일정한 속도를 유지해야 하는 로딩 애니메이션이 그렇다.
그리고 Spring
애니메이션은 아직 CSS
로 구현할 수 없기에 구현하기 위해서 Javascript를 사용해야 한다. Javascript
를 통해서 frame마다 스타일을 업데이트해야 한다.
만약 웹 페이지에서 무거운 연산이나 잦은 Javascript
실행을 하고 있다면 Spring
애니메이션을 사용하지 말아야한다. 되려 UX를 향상 시키려고 Spring
애니메이션을 도입했지만 UX를 해칠 수 있다.
이제 Spring
애니메이션에 대한 개념을 이해했으니 각자 spring visualizer에서 각 요소들을 조절해가며 Spring
을 좀 더 탐구 해보자.
그리고 더 관심이 간다면 react-spring 이나 framer-motion을 사용해서 애니메이션을 구현해 보길 권장한다.
최근에 react-spring
으로 Picker 컴포넌트를 구현했다. 이에 대한 글을 써서 실용적인 부분을 다뤄보려고 한다. 구현 영상을 첨부하며 글을 마무리한다.