Androidでポヨンするアニメーションをつくる

## はじめに
Life is Tech ! TOKAIのアドベントカレンダー12日目を担当するカーキです。
メンターとしては昨年卒業していますが、今はLife is Tech! School名古屋校のクラスマネージャーとして関わらせてもらっています。

突然ですが、みなさんプリンは好きですか？
僕は大好きです！
![sweets_purin_normal.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82263/e5870f6c-8e19-1837-3f18-8663a82b02e1.png)

例えばこんなプリンの画像を発見してしまうと、
どうしてもポヨンとしたくなってしまうのが、人としての性だと思います。

![poyon.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82263/ebf9893a-590c-687d-a458-b68a5dcd3048.gif)
こんな感じにです。

本記事ではこんなポヨンとするアニメーションをAndroidアプリで実現する方法を紹介します。

## SpringAnimationについて
今回プリンのポヨンアニメーションを実装するにあたり、バネのような動きを再現するため、[Spring Animation](https://developer.android.com/reference/androidx/dynamicanimation/animation/SpringAnimation)というGoogleから提供されているアニメーションライブラリを使用します。
以下の依存関係を追加することでSpringAnimationをプロジェクトに導入することができます。

```build.gradle
dependencies {
   def dynamicanimation_version = "1.0.0"
   implementation 'androidx.dynamicanimation:dynamicanimation:$dynamicanimation_version'
}  
```
執筆時点(2020年12月3日)では最新が`1.0.0`です。
`ktx`の開発も進められているようですが、アルファ版から[一年間更新がされていない](https://developer.android.com/jetpack/androidx/releases/dynamicanimation?hl=ja)ため、開発が止まっているかもしれません。本記事では`ktx`ではないバージョンで進めます。

### SpringAnimationとは何なのか
SpringAnimationではバネのような動きのアニメーションを物理学に基づいて実装することができます。
この時`DampingRaito`と`Stiffness`という２つのパラメータが重要になります。

#### DampingRaito
DampingRaitoとは日本語で減衰率という意味があり、この値を大きくすることで弾んでいる時間が長くなります。
DampingRaitoには定数として4つの値が用意されています。

- DAMPING_RATIO_HIGH_BOUNCY
- DAMPING_RATIO_MEDIUM_BOUNCY
- DAMPING_RATIO_LOW_BOUNCY
- DAMPING_RATIO_NO_BOUNCY

上からDampingRaitoが大きく、よく弾むようになります。

例として、それぞれの値でプリンを弾ませてみます。

| DAMPING_RATIO_HIGH_BOUNCY | DAMPING_RATIO_MEDIUM_BOUNCY | DAMPING_RATIO_LOW_BOUNCY | DAMPING_RATIO_NO_BOUNCY |
| ---- | ---- | ---- | ----- |
| ![purin_high_bounce.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82263/99c4a744-1505-ef7d-a93e-087c8560a68a.gif) | ![purin_medium_bounce.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82263/78b98ef8-36d1-cd6e-d076-dd3c13a5f49b.gif) | ![purin_low_bounce.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82263/e10d74cb-5ecd-0532-a637-966a7e1d8dcf.gif) | ![purin_no_bounce.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82263/47fee69d-bdd2-b72b-f2fe-4f5b0647c4f9.gif) |

#### Stiffness
Stiffnessとは剛性つまり、物体の硬さです。
この値が低いほど動きのぷるん感が増します。

- STIFFNESS_HIGH
- STIFFNESS_MEDIUM
- STIFFNESS_LOW
- STIFFNESS_VERY_LOW

こちらもプリンを弾ませてみます。

| STIFFNESS_HIGH | STIFFNESS_MEDIUM | STIFFNESS_LOW | STIFFNESS_VERY_LOW |
| ---- | ---- | ---- | ---- |
| ![stiffness_high.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82263/2180d357-902c-fe7c-b606-44382ed05394.gif) | ![stiffness_medium.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82263/86745f95-5a97-65f3-c241-bd509472efea.gif) | ![purin_medium_bounce.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82263/7ce55739-b8a3-7f26-a57a-fd380d3cff5c.gif) |![stiffness_low.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82263/8d7cd20d-4ce6-18fa-76b6-05031c0ebfb9.gif) | ![stiffness_very_low.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82263/f1061e34-4a6a-b812-802a-3d671bcbf295.gif) |

値が大きいほど剛性があるため、動きも硬くなっています。逆に値が小さいほどポヨンを感じられる動きになります。

## ポヨンを実現するために
### SpringAnimationの使い所
そもそもSpringAnimationはViewのプロパティの一部の変化に対してアニメーションを付与するものになります。
SpringAnimationが対応している[ViewProperty](https://developer.android.com/reference/androidx/dynamicanimation/animation/DynamicAnimation?hl=ja#fields_1)は以下の種類があります。

- ALPHA
- ROTATION
- SCALE
- SCROLL
- TRANSLATION
- 座標

それぞれにX, Y方向に関するプロパティが存在しています。

今回ポヨンを実現するためには、X, Y方向に関する大きさのプロパティを変えてやれば良いので、`SCALE_X`と`SCALE_Y`を使用します。

### 初期値を設定する
SpringAnimationでは`SCALE`プロパティを使用することで、A→Bへと大きさを変化させることができます。ただプリンをタップしてポヨンを実現するためには、アニメーション開始時にすでにプリンのスケールが変化している必要があります。
![プリン解説.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82263/6073af40-88f6-016a-eda0-b4f7ae8f8ee1.jpeg)

なのでまずプリンの大きさを60%くらいにします！

```PurinActivity.kt
binding.purinImage.scaleX = 0.6f
binding.purinImage.scaleY = 0.6f
```

### DampingRatioとStiffnessの設定
DampingRaitoとStiffnessはポヨンみを演出するとても大切なパラメータになります。
個人的には、上記のDampingRaitoとStiffnessの実際の動きを見て

- DampingRaitoは`DAMPING_RATIO_MEDIUM_BOUNCY`
- Stiffnessは`STIFFNESS_LOW`

が良いのではないかと思っています。
この条件で作成したポヨンが以下のものになります。

![stiffness_low.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82263/fd1db195-87ec-8fa9-3d0e-e8caa53f7b1a.gif)

実際のコードは次のようになります。

```PurinActivity.kt
private val xScaleAnimation: SpringAnimation by lazy {
   SpringAnimation(binding.purinImage, SpringAnimation.SCALE_X).apply {
      spring = SpringForce(1f).apply {
         dampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY
         stiffness = SpringForce.STIFFNESS_LOW
      }
   }
}

private val yScaleAnimation: SpringAnimation by lazy {
   SpringAnimation(binding.purinImage, SpringAnimation.SCALE_Y).apply {
      spring = SpringForce(1f).apply {
         dampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY
         stiffness = SpringForce.STIFFNESS_LOW
      }
   }
}

private fun startAnimation() {
   binding.purinImage.scaleX = 0.6f
   binding.purinImage.scaleY = 0.6f

   xScaleAnimation.start()
   yScaleAnimation.start()
}
```

あとは、プリンをタップした時にstartAnimation()を実行するだけです。


## さいごに
いかがだったでしょうか？

今回はViewのアニメーションにSpringAnimationを適用することでポヨンとしたアニメーションを作成しました。
大袈裟に60%の大きさに変更しましたが、80%, 90%にすることでボタンのタップ時のアニメーションにも応用することができそうです。

それでは、楽しいポヨンライフを！！