🙂

Jetpack Compose の Canvas でアニメーションするグラフをつくる

2021/10/17に公開

Jetpack ComposeでのCanvasを使った簡単なアニメーションするグラフを作ったメモです。
まだまだ勉強中なのでもっとスマートな書き方あったら教えて下さい👍

Jetpack ComposeでCanvasを利用する

公式ドキュメント

Canvas(modifier = Modifier.fillMaxSize()){
	drawRect(..)
	drawArc(..)
}

Canvas Composableファンクションが用意されているので他のComposableファンクションと同様に利用します。
第2引数にはDrawScopeが公開している描画エリアのサイズや描画用メソッドを描きたい図形に合わせて呼び出します。

棒グラフを描画してみる

ここから本題です。まずはCanvasで棒グラフを描画してみます。

drawRect()を利用して棒グラフを描画します。このときグラフにアニメーションを付けたときに画面下から上にグラフが伸びるようにするためにsizeのy軸にマイナス値を指定し、画面上方向に描画されるようにしています(他に良い方法があるかも)。

@Composable
fun drawGraphSample(){
	val sampleValues = listOf(300f, 500f, 700f)

	Canvas(modifier = Modifier.fillMaxSize().padding(start = 32.dp, bottom = 32.dp)){
	    drawRect(
		topLeft = Offset(0f, size.height),
		color = Color.Red,
		size = Size(200f, -sampleValues[0]),
	    )
	    drawRect(
		topLeft = Offset(250f, size.height),
		color = Color.Red,
		size = Size(200f, -sampleValues[1]),
	    )
	    drawRect(
		topLeft = Offset(500f, size.height),
		color = Color.Red,
		size = Size(200f, -sampleValues[2]),
	    )
	}
}

棒グラフに簡単なアニメーションをつけてみる

下記のように値が0から1まで変化するanimateFloatAsState用意し、棒グラフのy軸の描画に利用することでグラフの高さが0%から100%までアニメーションさせることができます。
公式ドキュメント

@Composable
fun drawGraphSample(){
        val animationFlag = remember { mutableStateOf(false)}
        val animateHeight by animateFloatAsState(if (animationFlag.value)  1f else 0f)
        val sampleValues = listOf(300f, 500f, 700f)

        Canvas(modifier = Modifier.fillMaxSize()){
            animationFlag.value = true
            drawRect(
                topLeft = Offset(0f, size.height),
                color = Color.Red,
                size = Size(200f, animateHeight * -sampleValues[0]),
            )
            drawRect(
                topLeft = Offset(250f, size.height),
                color = Color.Red,
                size = Size(200f, animateHeight * -sampleValues[1]),
            )
            drawRect(
                topLeft = Offset(500f, size.height),
                color = Color.Red,
                size = Size(200f, animateHeight * -sampleValues[2]),
            )
        }
}

また、animationSpecを指定することでアニメーションの仕様を変えることもできます。

val animateHeight by animateFloatAsState(
    targetValue = if (animationFlag.value)  1f else 0f,
    animationSpec = tween(durationMillis = 500, easing = FastOutSlowInEasing)
)

終わりに

Canvasも他のComposableファンクション同様にanimate*AsStateを利用することで簡単にアニメーションさせることができました。
他にもグラフの色や幅も同様にアニメーションさせることもできるので今後試していきたいです。

Discussion