はじめに
Compose でバーが左端から右に伸びるアニメーションを実装しようとしたところ、左端がずれる問題に遭遇したので、原因と解決方法を記事に残します。
問題
graphicsLayer { scaleX = progress } を使うと、デフォルトのスケール点が中央なので両端から縮む動きになり、左端が右にずれてしまう。
// 中央からスケールされて左端がずれる
Box(
modifier = Modifier
.width(totalWidth)
.graphicsLayer { scaleX = progress }
)
原因
Compose の graphicsLayer にも transformOrigin パラメータがあるが、Canvas
で複数バーを描画している場合は個別に効かせにくい。
(iOS の SwiftUI には scaleEffect(anchor: .leading)でスケール点を左端に指定できる。)
解決策
Modifier でスケールするのをやめ、Canvas の drawRect で幅だけを progress に応じて変化させる。x(左端座標)は固定、w(幅)だけアニメーションするように。
val animProgress = remember(key) { Animatable(0f) }
LaunchedEffect(key) { animProgress.animateTo(1f, tween(400)) }
val progress = animProgress.value
Canvas(modifier = Modifier.fillMaxSize()) {
val x = size.width * bar.startFraction // 左端は固定
val w = size.width * bar.widthFraction * progress // 横幅だけ伸ばす
drawRect(
color = barColor,
topLeft = Offset(x, 0f),
size = Size(w.coerceAtLeast(0f), size.height),
)
}
Modifier のスケール系は原点が中央になるので、左端固定で伸ばしたいときは Canvas で幅を直接計算するほうがシンプル。