Fluth|商品券付加3 Dインタラクション


ギフト券


トス商品券を購入するページに面白いインタラクションがあります.
購入数を変更するたびに3 Dアニメーション効果が発生します.

ソース:https://blog.toss.im/article/ux-engineer-interview
📌 このページでは、重要なインタラクティブな部分を真似します.
1▼▼▼▼▼選択数ページ初期勾配変化アニメーション
2▼▼▼▼▼ボタン数を変更した場合、ボタンの大きさをアニメーション化
3▼▼▼数を変更した場合、カードを追加する動画

Flutter Rendering


まず、Flutterレンダリングについて説明します.
私たちが見た部分は2 Dのように見えますが、Flutterはもともと3 Dレンダリングされていました.
Googleの説明でも、性能が非常に悪いスマートフォンのほか、ほとんどのスマートフォンには3 Dグラフィックスを最適化する高速GPUが含まれていると述べています.つまり、3 Dグラフィックスのレンダリング速度が非常に速いため、Flutterも3 Dレンダリングを使用しています.
🔥 そのため、3 Dアニメーション効果、3 D画像モデリング、ゲーム制作をFluterで行うことができます.

カード読み込みインタラクション


初めてページに入ったとき、カードは水平で、ゆっくりと傾いていました.
これを実現するために、AnimationControllerとTransform.rotate部品で実現しました.

Transform.rotate


✅ Transform.rotateは回転を中心として、回転を使用してサブアセンブリを作成します.
✅ Transform.rotateの角度パラメータはnullではなく、角度値は時計回りの弧度単位の回転を提供します.
アニメーション効果を制御できるアニメーションコントローラが生成されます.
AnimationControllerが受信した値を使用して傾斜を調整します.
// 카드 애니메이션.
late AnimationController _cardRotateController = AnimationController(
  vsync: this,
  duration: const Duration(seconds: 1),
)..addListener(() => setState(() {}));


void initState() {
  super.initState();
  _cardRotateController.forward();
}
」」」」」」」」」」」
このエラーは、TickerProviderを割り当てることができないためのエラーであり、TickerProviderを割り当てると簡単に解決できます.
class _GiftCardPageState extends State<GiftCardPage> with TickerProviderStateMixin {}
上で生成したアニメーションコントローラで回転値を指定します.
これは、AnimationControllerの初期値が0.0で、アニメーションの順方向が1.0に変更された点です.
Transform.rotate(
  angle: _cardRotateController.value * (-math.pi / 30),
  child: _giftCard...
)

ボタンをクリックして操作


ボタンをクリックすると、ボタンのサイズが大きくなり、元のサイズに戻ります.
このアニメーション効果を与えるためにTransformscaleを用いて大きさの変化の効果を生じた.

Transform.scale


892 D平面に沿ってサブアセンブリのサイズを調整できるコンポーネントを作成します.
2989 scaleXパラメータはx軸に乗じたスカラーを提供し、scaleYパラメータはy軸に乗じたスカラーを提供します.どちらも省略できますが、この場合、軸のデフォルト値は1.0です.
scaleXとscaleYを指定することなく、scaleパラメータを使用してサブアイテムのサイズを統一できます.
少なくとも1つはnullではない必要があります.scaleが提供された後、他の2つはnullでなければなりません.同様に、提供されていない場合は、別のものを提供する必要があります.
[位置合わせ](Align)は、メッシュの原点を制御します.既定では、これはボックスの中心です.
増加ボタンとボトムボタンの大きさはそれぞれ変化するので、AnimationContollerも1つずつ生成します.
AnimationControllerの生成時には、低Boundと高Boundの値も指定されます.
📌 upper bound:vsync: thisで得られた最大値.
📌 LowerBound:The argument type '_GiftCardPageState' can't be assigned to the parameter type 'TickerProvider'で得られた最小値.
// 증감 버튼 크기 애니메이션.
late AnimationController _decreaseButtonScaleController;
late AnimationController _increaseButtonScaleController;

// 바운스 애니메이션.
final Duration _bounceAnimationDuration = const Duration(milliseconds: 100);

// 버튼 크기 애니메이션 설정.
void _setButtonScaleAnimation() {
  // 감소 버튼 애니메이션.
  _decreaseButtonScaleController = AnimationController(
    vsync: this,
    duration: _bounceAnimationDuration,
    lowerBound: 0.0,
    upperBound: 0.6,
  )..addListener(() => setState(() {}));

  // 증가 버튼 애니메이션.
  _increaseButtonScaleController = AnimationController(
    vsync: this,
    duration: _bounceAnimationDuration,
    lowerBound: 0.0,
    upperBound: 0.6,
  )..addListener(() => setState(() {}));
}
  

void initState() {
  super.initState();
  ...
  _setButtonScaleAnimation();
}
商品券の数を増やしてボタンを減らすと、
animationControllerへ進むcontroller.value手法を実現した.
放送終了後、後方に進むcontroller.valueが放送された.
[アニメーションコントローラ](Animation Controller)は、前後に、毎回アニメーションコントローラを使用します.value値は、上記で指定した0.0および0.6になります.
CupertinoButton(
  onPressed: () {
    // 애니메이션 효과.
    animationController.forward();
    Future.delayed(_bounceAnimationDuration, () {
      animationController.reverse();
    });

    // 값 변경.
    ...

	// 카드 바운스 애니메이션.
    ...
  },
  child: Transform.scale(
    scale: 1 + animationController.value,
    child: Container(
      padding: const EdgeInsets.all(4),
      decoration: BoxDecoration(
        shape: BoxShape.circle,
        color: Colors.grey.withOpacity(0.2),
      ),
      child: Icon(
        iconData,
        size: 36,
        color: Colors.white,
      ),
    ),
  ),
)

カードインタラクションの追加


カードの数が変わるたびに、カードが一枚ずつ積み重なっていきます.
そして、積み重ねるごとにカード全体が弾かれるアニメーションもあります.
カードと3 Dアニメーションを追加するにはTransformコンポーネントを使用します.

Transform


サブアイテムを描画する前に、フォーマットのコピーを適用するコンポーネント.
このオブジェクトは、テクスチャレイアウトの前に回転を適用するRotatedBoxとは異なり、塗りつぶしの前に変形を適用します.

カードの追加


Stack部品で数量を変更するたびに、Stackサブアイテムも変更方法を使用します.
そして、最初のカードはいつも上に積まれているので、Stackの子供たちを逆順に並べます.
最初のカードはいつも上が見えます.
Stack(
  children: List.generate(
    _animationCardQuantity,
    (index) {
      return _giftCard(index: index),
      );
    },
  ).reversed.toList(),
)
▼しかし、このように積むと同じ位置に積まれるので、実際には同じように見えます.そのため、カードごとに位置が異なります.
3 D効果を与えるために、カードの位置と回転の変化を与えます.
カードの位置をX軸とY軸に移動します.
2
return Transform(
  transform: Matrix4.identity()
    // 이동.
    ..setEntry(0, 3, -1.5 * _index)
    ..setEntry(1, 3, -2.0 * _index)
    // 회전.
    ..rotateX(-0.075 * _index)
    ..rotateY(0.07 * _index),
  child: _giftCard(index: index),
);

カード3 D弾き動画


カードが1枚増えるごとに、1枚落ちるごとに3 D効果が変化し、カード全体が跳ね返る効果があります.
イジェクトは2 Dではなく3 Dなので、ボタンをクリックしたアニメーションとは異なるはずです.
3 Dアニメーションを与えるために、回転機能で弾きます.
late AnimationController _cardBounceController;

// 카드 바운스 애니메이션.
_cardBounceController = AnimationController(
  vsync: this,
  duration: _bounceAnimationDuration,
  lowerBound: 0.0,
  upperBound: 10.0,
)..addListener(() => setState(() {}));

// 카드.
Transform(
  alignment: Alignment.topLeft,
  transform: Matrix4.identity()
  // 회전.
  ..rotateX(_cardBounceController.value * (-math.pi / 400))
  ..rotateY(_cardBounceController.value * (math.pi / 400)),
  child: Stack(
    children: ...
  ).reversed.toList(),
),
アニメーション効果の進行と後退は、他のアニメーションと同じです.
// 증감 버튼.
CupertinoButton(
  onPressed: () {
    // 버튼 크기 애니메이션 효과.
    ...

    // 값 변경.
    ...

    // 카드 바운스 애니메이션.
    _cardBounceController.forward();
    Future.delayed(_bounceAnimationDuration, () {
      _cardBounceController.reverse();
    });
  },
  child: 증감 버튼... ,
)

Github
https://github.com/CHOI-YOOBIN/toss_gift_card_interaction