웹 애니메이션은 사용자에게 좋은 경험을 제공하는데 도움을 줄수 있다. 애니메이션을 공부하면서 UI 애니메이션 속도를 300ms 이하로 유지하라고들 한다. 이유는 애니메이션의 재생 시간이 길어지면 사용자가 앱이 느리다고 느끼기 때문이라고 한다. 별 생각없이 그렇구나 하고 적용했다.
그런데 슬라이드 애니메이션 예제를 보았다. 내가 생각했을때 속도가 250ms정도에 easing out이 적용되어있을 것이라고 생각했는데, 코드를 보니까 속도가 500ms 인데 custom easing을 극단적으로 조절해 놓은 것이었다. 초반 부터 종반까지 정말 빠르게 휙하고 움직이지만 마지막 시점에서 속도도 빠르게 느껴지고 마지막에 댐핑이 적용된 것같이 부드럽게 마무리가 되었다.
그 예제를 보고나서 css에서 easing에 적용하는 cubic bezier 함수의 원리가 궁금해졌다. 나는 easing에 왜 베지어 곡선을 적용해야하는지 어떤 원리로 그냥 곡선이 애니메이션의 느낌을 바꿀수 있는지 궁금했다. 그래서 베지어 곡선이 무엇이고, 어떤 원리로 easing에 쓰일수 있는지 알아보기로 했다.
베지어 곡선이란
베지어 곡선은 컴퓨터가 곡선을 그릴때 사용할 수 있는 수학적 함수이다1. 여러 개의 제어점을 기준으로, 시작점에서 끝점까지 부드럽게 이어지는 곡선을 만들수 있다. 베지어 곡선은 벡터 그래픽에 곡선을 모델링하고, 애니메이션, 사용자 인터페이스 디자인, 시선 추적 기반 인터페이스에서 커서의 움직임을 부드럽게 만드는데 사용되기도 한다.
베지어 곡선 특징
- 조절점이 항상 곡선위에 있지 않다.
- 곡선의 차수는 조절점의 개수에서 1을 뺀 값이다.
- 곡선은 항상 조절점 위의 컨벡스 헐안에 있다.
특히 컨벡스 헐 안에 있다라는 특징이 있어 점을 무작위로 겹치지 않게 N개를 찍어도 곡선은 N개의 점을 이은 면 안에 반드시 그려진다.
카스텔조 알고리즘2
어떻게 점 4개로부터 베지어곡선을 그릴 수 있을까?
- 평면 위에 점 4개를 찍어보자. 그리고 ABCD 점을 AB, BC, CD로 이어보자.
- 매개변수 t는 0 ~ 1의 값의 범위에서 움직인다.
- t를 0.1로 변경한다. 각 선분의 길이의 0.1배에 해당하는 곳에 점을 찍는다. 그럼 총 3개의 점이 찍힌다.
- 그 0.1배에 해당하는 찍힌 점들을 선으로 잇는다. 각각 EF, FG가 된다.
- EF, FG의 선분위에 0.1배에 해당하는 곳에 점을 찍는다. 이 점은 K, L이다. 이 두점을 잇는다.
- KL 선분위에 0.1배에 해당하는 위치에 점을 찍는다. 이 점은 Z다.
- t의 값을 0.15로 변경한다. 2~4를 반복한다.
이렇게 하면 Z의 궤적이 그려지는데 이 궤적은 곧 곡선이된다. 평면위에 4개의 점을 찍은 예시로 했지만 점이 N개면 N-1개로 점을 줄여가면 된다.
① t = 0.25 — 각 선분의 0.25 지점에 점 E·F·G를 찍는다
아래로 스크롤하면 단계가 하나씩 진행됩니다.
easing function
cubic-bezier(3차 베지어 곡선)
큐빅 베지어 easing function은 점 4개로 이루어진 베지어 곡선의 두 조절점의 좌표로 베지어 곡선을 만들어내는 함수이다. P0, P1, P2, P3의 점을 찍는데 P0은 (0,0) P3는 (1,1)로 고정되어있다고 가정한다. cubic-bezier 함수의 값은 P1의 x,y값 P2의 x,y값을 나타낸다.
cubic-bezier(x1, y1, x2, y2)
조절점 두개로 곡선의 모양을 결정할 수 있다. x는 반드시 0-1 사이 값이어야하고 그 이상, 이하가 되면 css는 이 값을 무시한다. y는 값이 어떻게 되든 상관 없는 것 같다. 참고로 y는 음수도 들어갈 수 있다3.
베지어 곡선은 어떻게 같은 시간 값을 가진 애니메이션의 속도를 느리거나 빠르게 보이게 할수 있을까?
큐빅 베지어가 그린 베지어 곡선과 duration은 어떤 관계가 있는 것일까? 왜 똑같은 duration이지만 베지어 곡선의 모양에 따라 애니메이션이 진행되는 모양이 다른걸까?
베지어 곡선의 x는 애니메이션이 진행되는 시간이다. 이건 duration과 1대1로 매칭되는 시간이 아니라 정규화된 시간이다4. duration이 얼마이건간에 관계 없이 x는 0-1의 값을 가진다. duration이 빠르면 x가 0에서 1로 가는 시간도 빠르다. 그리고 y는 애니메이션의 진행률이다. x위치에 애니메이션 대상이 어디까지 진행되는지는 y를 보면 알수 있다. 예를 들어 오른쪽으로 200px만큼 움직이는 애니메이션인데 x가 0.1인데 y가 0.9이면 180px만큼 움직였다고 보면 된다. 이 애니메이션은 매우 빠르게 느껴질 것이다. 그런데 x가 0.2인데 y가 갑자기 0.2가 되면 애니메이션은 다시 뒤로 이동하게 된다. 만약 y가 1.4라면 매우 과장되게 280px만큼 이동할 것이다. 기울기는 애니메이션의 순간 속도로 본다.(물리에서 위치-시간 그래프의 기울기가 속도인 것과 같다. 기울기가 가파를수록 그 순간 빠르게 움직인다.) 그래서 베지어 곡선을 직선으로 만들면 애니메이션의 속도가 일정하고, 위로 볼록하게 만들면 초반 애니메이션의 진행이 빨라지고 아래로 볼록하면 초반 진행이 느려진다.
이 원리를 알면 대충 어떻게 점을 찍으면 애니메이션이 어떤식으로 될것 같다는 상상을 하는 것도 가능하다고 생각한다. 특히 AI에게 내가 원하는 custom easing을 설명할때 좋은 것 같다. 하지만 가장 빠르고 정환한건 애니메이션이 어떻게 동작하는지 예시를 찾아서 그 베지어 곡선을 적용하는 것이 가장 빠르다는 생각도 든다. 예시 사이트에서 직접 동작해보고 감을 익혀보자.5
예시
아래 두 모바일 화면에서 버튼을 눌러 bottom sheet를 올려보면, duration이 2.5배나 차이 나는데도 두 애니메이션이 거의 비슷한 속도로 느껴진다.
cubic-bezier(0, 0, 0.2, 1)cubic-bezier(0.16, 1, 0.3, 1)- ease-out
cubic-bezier(0, 0, 0.2, 1)· 200ms — 실제로 짧은 애니메이션이다. 처음엔 빠르게 튀어나오고 끝에서 감속하며 부드럽게 멈춘다.
.sheet {
transition: transform 200ms cubic-bezier(0, 0, 0.2, 1);
}
- ease-out (extreme)
cubic-bezier(0.16, 1, 0.3, 1)· 500ms — duration은 오히려 2.5배 길지만, 두 번째 조절점의 y값을 1까지 끌어올려 곡선을 초반부터 거의 수직으로 세워 놓았다. 그래서 진행률이 앞쪽(약 200ms 지점)에서 이미 90% 넘게 차오르고, 남은 300ms는 마지막 몇 %를 아주 천천히 채우며 댐핑처럼 부드럽게 마무리된다.
.sheet {
transition: transform 500ms cubic-bezier(0.16, 1, 0.3, 1);
}
결과적으로 500ms짜리인데도 200ms짜리와 거의 같은 타이밍에 "휙" 하고 도착한 뒤 살짝 정착하는 느낌을 준다. 시간(duration)은 그대로 두고 곡선만으로 체감 속도를 이렇게까지 바꿀 수 있다는 것이 cubic-bezier의 핵심이다. 실제로 오른쪽이 훨씬 더 부드럽고 자연스럽게 느껴진다.
그동안 애니메이션을 적용할때 단순하게 애니메이션 duration이 느리면 앱이 느리게 느껴진다는 공식을 가지고 애니메이션을 적용해왔다. 하지만 애니메이션을 공부하면서 easing function이 어떻게 적용되었느냐에 따라 250ms보다 더 느리지만 심리적으로 빠르고 부드럽게 보이는 애니메이션을 적용할수 있다는 것을 알게 되었다. 베지어 곡선이 무엇이고 원리도 알게 되었고 베지어 곡선이 컴퓨터 그래픽스에서 곡선을 그릴때 아주 유용한 이론이라는 것도 알게 되었다.6
'cubic-bezier 말고 다른 종류의 css easing 함수가 있을까'하는 궁금점도 있었는데 찾아보니 종류가 단축어와 cubic-bezier를 제외하고 linear()와 steps()이 있다. 본문의 주제와는 조금 달라서 예시는 링크만 첨부한다.7.
Footnotes
-
베지어 곡선 — The Modern JavaScript Tutorial, https://ko.javascript.info/bezier-curve ↩
-
카스텔조 알고리즘 — The Modern JavaScript Tutorial, https://ko.javascript.info/bezier-curve#ref-847 ↩
-
cubic-bezier() — MDN Web Docs, https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function/cubic-bezier ↩
-
Josh Collinsworth, "Understanding easing and cubic-bezier curves in CSS", https://joshcollinsworth.com/blog/easing-curves ↩
-
Gallery Carousel(예시 사이트) https://easings.co/ ↩
-
Wikipedia, "Bezier curve" , https://en.wikipedia.org/wiki/B%C3%A9zier_curve# ↩
-
Easing Wizard — CSS easing function 예시, https://easingwizard.com/ ↩