【Jetpack Compose】3本線のローディングアニメーション作ってみた

# はじめに
Jetpack Composeでアニメーションとか作りたいなーと思い、
まずは簡単そうなものから作ってみることにした。
たまーに業務でアニメーションを実装する機会があり、その度に手こずりまくるので…
※AIは使用不可

# やりたいこと
以下のような3本線のローディングアニメーションを作る

![Videotogif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308202/72f6add8-40d2-4678-912c-5ab79d0b3888.gif)

# 実現に向けて
以下のような方法でこのアニメーションを実現させる。

3つの線の高さ（Height）をそれぞれ動的に描画し、
それぞれの線のアニメーション開始タイミングには時差を設けて、

1つ目の線のアニメーション開始
▼
2つ目の線のアニメーション開始(1つ目のオブジェクトの描画から100ミリ秒後)
▼
3つ目の線のアニメーション開始(1つ目のオブジェクトの描画から200ミリ秒後)

といった順番に描画させる。
また、アニメーションの作成には、`animate*AsState`と呼ばれるAPIを使用する。

**▼参考**

https://developer.android.com/develop/ui/compose/animation/value-based?hl=ja#:~:text=animate*AsState%20%E9%96%A2%E6%95%B0%E3%81%AF%E3%80%81Compose,%E5%8C%96%E3%82%92%E9%96%8B%E5%A7%8B%E3%81%97%E3%81%BE%E3%81%99%E3%80%82

# 実装
下記のようにアニメーションの関数をComposeで用意する。

```Kotlin
// アニメーション描画させる線1本分のデータクラス
data class LoadingItemInfo(
    val height: Dp,
    val width: Dp,
    val color: Color
)

// アニメーションの関数（3本線のView）
@Composable
fun LoadingAnimation() {
    // 3本線のリスト（色もそれぞれ異なるものを指定）
    val boxList = listOf(
        LoadingItemInfo(
            startAnimationDp(0),
            10.dp,
            colorResource(R.color.purple_200)
        ),
        LoadingItemInfo(
            startAnimationDp(1),
            10.dp,
            colorResource(R.color.purple_500)
        ),
        LoadingItemInfo(
            startAnimationDp(2),
            10.dp,
            colorResource(R.color.purple_700)
        )
    )

    // Viewの作成
    Row(
        modifier = Modifier.fillMaxSize(),
        horizontalArrangement = Arrangement.Center,
        verticalAlignment = Alignment.CenterVertically
    ) {
        boxList.forEachIndexed { index, info ->
            Box(
                Modifier
                    .height(info.height)
                    .width(info.width)
                    .background(info.color)
            )

            // 3本線の間隔調整（1つ目と2つ目の線の間に10Dpの間隔調整）
            if (index != boxList.size - 1) {
                Spacer(modifier = Modifier.width(10.dp))
            }
        }
    }
}

// 上下するHeightサイズ（Dp）を返却する関数
@Composable
fun startAnimationDp(
    index: Int,
    minHeight: Dp = 40.dp,
    maxHeight: Dp = 80.dp,
    durationMills: Int = 400
): Dp {
    // サイズの状態
    var expanded by remember { mutableStateOf(false) }

    // Dpサイズをアニメーションで動的に変更
    val scaleDpSize by animateDpAsState(
        targetValue = if (expanded) minHeight else maxHeight,
        animationSpec = infiniteRepeatable(
            animation = tween(durationMills),
            repeatMode = RepeatMode.Reverse
        ),
        finishedListener = { expanded = true }
    )

    // 3本線リストのインデックス値に応じてアニメーションタイミングを変更
    LaunchedEffect(true) {
        delay(index * 100L)
        expanded = true
    }
    return scaleDpSize
}
```

あとはsetContentとかで作成した`LoadingAnimation()`を呼び出すだけ。
```Kotlin
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Column(
                Modifier.fillMaxSize(),
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                LoadingAnimation()
            }
        }
    }
```

# 所感
AIが発達してもっと手間暇なく実装できそうな気がするけど、
やり方や考え方を学ぶという意味では今後も作成してみる価値がありそうだなーと思ってみたり。
もしかしたらまた何か作ってみるかもです。