더블 스프링: 자연스러운 Ease-In-Out
•문대승
springanimationeasing
싱글 스프링의 한계
일반적인 스프링 애니메이션을 생각해보세요. 용수철에 매달린 물체가 목표 지점을 향해 움직입니다.
힘 = -stiffness × (현재위치 - 목표)
문제는 시작 순간입니다. t=0일 때 현재 위치와 목표의 거리가 가장 멀기 때문에, 힘이 최대가 됩니다. 물체는 처음부터 급격하게 가속합니다.
시작 ●━━━━━━━━━━━━━━━━━━━━━━━━━━━━● 목표
↑
거리가 최대 = 힘이 최대 = 급출발!
이것이 스프링 애니메이션이 "ease-out" 느낌만 주는 이유입니다. 빠르게 시작해서 천천히 끝나죠.
더블 스프링의 해결책
더블 스프링은 두 개의 스프링을 연결합니다:
목표 ←―[리더 스프링]―← 리더 ←―[팔로워 스프링]―← 나(출력)
- 리더: 목표를 직접 추적
- 팔로워(나): 리더를 추적 (이것이 실제 출력값)
시간에 따른 동작
t=0 (시작)
목표 리더 나
●━━━━━━━━━━●━━━━━━●
↑
리더가 먼저 출발
나와 리더 거리 = 작음
→ 나에게 가해지는 힘 = 작음
→ 천천히 출발! (ease-in)
t=중간
목표 리더 나
●━━━━━━●━━━━━━━━━━━━━━━━●
←――――――――――――→
거리가 벌어짐
→ 힘이 커짐
→ 가속!
t=끝
목표/리더 나
●━━━━━━━━━━━━━━━━━━━━●
↑
리더가 먼저 도착해서 느려짐
나와 리더 거리 = 다시 줄어듦
→ 나도 감속 (ease-out)
비유로 이해하기
싱글 스프링 = 고무줄로 목표에 직접 연결
- 처음에 고무줄이 최대로 늘어나서 확 당겨짐
더블 스프링 = 앞에 가이드가 있고, 나는 가이드를 따라감
- 가이드가 먼저 출발해도 나와 가이드 사이 거리는 처음엔 작음
- 가이드가 앞서가면서 점점 당겨지고
- 가이드가 도착 근처에서 느려지면 나도 자연스럽게 감속
코드로 보면
// 매 프레임마다:
// 1단계: 리더가 목표를 쫓음
leader = stepSpring(leader, target, constants)
// 2단계: 나(팔로워)가 리더를 쫓음 (목표가 아니라!)
follower = stepSpring(follower, leader.position, constants)
// follower.position이 실제 출력값
핵심은 **팔로워 입장에서 목표가 "움직이는 목표(리더)"**라는 점입니다. 리더가 처음엔 천천히 출발하니까 나도 천천히, 리더가 중간에 빨라지면 나도 빨라지고, 리더가 끝에서 느려지면 나도 느려집니다.
SSGOI에서 사용하기
// 기본 더블 스프링 (같은 stiffness)
{
stiffness: 180,
damping: 22,
doubleSpring: true
}
// 강한 ease-in (팔로워 stiffness가 절반)
{
stiffness: 180,
damping: 22,
doubleSpring: 0.5
}
// 더 강한 ease-in
{
stiffness: 180,
damping: 22,
doubleSpring: 0.3
}
doubleSpring 값이 작을수록 팔로워의 stiffness가 낮아져서, 리더를 더 느리게 따라갑니다. 이는 더 강한 ease-in 효과를 만들어냅니다.
언제 사용하면 좋을까?
| 상황 | 추천 |
|---|---|
| 빠른 반응이 중요 | 싱글 스프링 |
| 부드러운 시작/끝이 중요 | 더블 스프링 |
| 고급스러운 UI 전환 | 더블 스프링 |
| 게임 캐릭터 이동 | 싱글 스프링 |
페이지 전환이나 모달 애니메이션처럼 "디자인된" 느낌이 중요한 곳에서 더블 스프링을 사용하면 좋습니다.
수학적 배경
싱글 스프링의 응답은 지수 감쇠입니다:
x(t) = 1 - (1 + γt)e^(-γt)
더블 스프링은 이 응답을 **두 번 적용(convolution)**한 것과 같습니다. 결과적으로 더 높은 차수의 다항식이 곱해진 지수 감쇠가 되어, S-curve 특성을 만들어냅니다.
물리적으로는 두 개의 질량-스프링 시스템을 직렬로 연결한 것과 같습니다. 첫 번째 스프링이 외부 힘을 흡수하고, 두 번째 스프링은 완충된 힘만 받습니다.