역량 강화를 위해 팀원과 디자이너와 함께 진행한 첫 애니메이션 효과는 통통 튀는 바운스 애니메이션!(전체 코드 확인은 깃허브에서 확인 가능~)
위에 gif가 내가 만든 애니메이션! (뿌듯...🧡)
디자이너가 피그마에 올려준 디자인과 인터렉션 효과!
👉 주황색 원형을 탭했을 때 원형 모양이 커지면서 통통 튕기는 듯한 효과가 나타나고 다시 원래 크기로 되돌아오는 것을 미션으로 받았다.
작은 원형 → 큰 원형 → 작은 원형
처음엔 단순히 원형이 커지고 작아지는 것에만 초점을 맞춰서 140 → 219 → 140이 되는 구현에 신경썼다.
그랬더니 그냥 일정한 속도로 커지다가 갑자기 뚝하고 140으로 되돌아오는 모습으로 구현됐다. (어색하기 짝이 없었고, 요청된 내용과도 차이가 있어보였다.)
// 피그마를 보면 Stiffness, Damping, Mass 값이 존재하는 것을 알 수 있다.
sizeAnimation = Tween<double>(begin: 140, end: 219).animate(
CurvedAnimation(
parent: sizeAnimationController,
// curve: Curves.easeOut, => 기본 사용
// SpringSimulation 대신 Cubic 베지어 곡선 이용
curve: const Cubic(0.23, 0.86, 0.29, 1),
),
);
이를 고려해 직접 값을 줘서 구현해주기로 했다.
GPT를 통해 curve 구현에 도움을 받기로 한다...
SpringSimulation 또는 Cubic 베지어 곡선으로 구현 가능했는데, 코드를 보니 Cubic 베지어 곡선이 더 간결해보여 이것으로 선택했다.
✅ sprung 라이브러리
팀원이 좋은 라이브러리를 찾아냈다!
Spring animation을 구현해주는 라이브러리로 피그마에서 mass, stiffness, damping 값을 넘겨주면 알아서 적절한 커브를 만들어준다고 한다.extendAnimation = Tween<double>(begin: start, end: end).animate( CurvedAnimation( parent: extendController, curve: Sprung.custom( mass: 1, stiffness: 250, damping: 31.6, ), ), );
final curve = FigmaSpringCurve(250, 31.6, 1); final bouncyCurve = FigmaSpringCurve.bouncy;
② 통통 튕기는 Curve
이건 팀원이 GPT를 통해 알아낸 것을 긁어왔다.
솔직히 이 부분은 정확히 어떻게 구현해야할지 감이 안 오는 부분이다...
import 'dart:math';
import 'package:flutter/material.dart';
class CustomBounceCurve extends Curve {
// t => 애니메이션의 진행 비율
double transform(double t) {
const double amplitude = 0.5; // 진폭
const double period = 0.4; // 주기
// 초기 조건
if (t == 0.0) return 0.0; // 애니메이션이 시작될 때
if (t == 1.0) return 1.0; // 애니메이션이 끝날 때
// 바운스 애니메이션 계산
return 1.0 -
amplitude *
pow(2.0, -10.0 * t) *
sin((t - period / 4.0) * (2 * pi) / period);
}
}
원형이 커질 때 통통 튕기는 Curve 동시에 적용하기
처음엔 AnimationController를 하나만 만들어서 동시에 구현할 수 있을 거라 생각했는데, 그러면 a 애니메이션이 끝나고 b 애니메이션이 진행되는 형태인 것 같았다.
그래서 AnimationController를 각각 만들어주고 a 애니메이션과 b 애니메이션이 동시에 진행될 수 있도록 했다. (a 애니메이션 = 원형 사이즈 / b 애니메이션 = 통통 튕기는 커브)
// 원래 크기로 복귀
sizeAnimationController.addStatusListener(
(status) async {
if (status == AnimationStatus.completed) {
// sizeAnimationController.reverse();
await Future.delayed(const Duration(microseconds: 200));
bounceAnimationController.forward();
}
},
);
(+) 의문점🤔
코드를 보니 동시에 진행이라기보단, a 애니메이션이 끝나고 b 애니메이션이 진행되는 형태인 것 같은데...
왜 나는 AnimationController를 각각 만들어주고 a 애니메이션과 b 애니메이션이 동시에 진행되게 만들었다고 생각했던걸까?
솔직히 나에겐 어려웠던...
그래서 팀원의 코드를 참고했고, GPT의 도움도 많이 받았다.
언젠간 나도 안 보고도 챡챡 코딩해내고싶다!
(+) 추가 내용