🌟
Jetpack ComposeでShimmer Effectを作る
はじめに
Shimmer Effectとは画像のような、スケルトンスクリーンをキラキラ光らせるエフェクトです。
コンテンツのロード中であることがより視覚的にわかるのでユーザーエクスペリエンスの向上が期待できます。
実装
fun Modifier.shimmer(
animationSpec: InfiniteRepeatableSpec<Float> = infiniteRepeatable(
animation = tween(1500)
),
colors: List<Color> = listOf(
Color(0xFFB8B5B5),
Color(0xFFC0C0C0),
Color(0xFFD3D3D3),
Color(0xFF808080),
Color(0xFFB8B5B5)
)
): Modifier = composed {
var size by remember { mutableStateOf(IntSize(0,0)) }
val startOffsetX by rememberInfiniteTransition().animateFloat(
initialValue = -2 * size.width.toFloat(),
targetValue = 2 * size.width.toFloat(),
animationSpec = animationSpec
)
val linearGradient = Brush.linearGradient(
colors = colors,
start = Offset(startOffsetX, 0f),
end = Offset(startOffsetX + size.width.toFloat(), size.height.toFloat())
)
background(linearGradient)
.onGloballyPositioned { size = it.size }
}
引数
-
animationSpec
はエフェクトが始まってから終わるまでの所要時間です。デフォルトで1500msとしていますが、お好みで変更可能です。およそ1000ms~1500ms位が妥当だと思われます。 -
colors
はエフェクトで遷移する色の配列です。最初の要素と最後の要素は同じ色にしておくとスムーズに感じます
内部
-
composed{ }
関数はModifierメソッド内でremember
などのステートフルな状態を扱う関数を使用可能にしてくれます。通常、remember
などのComposable関数は@Composableアノテーションがついていない関数では使うことはできませんがcomposed{ }
はそれを可能にしてくれます -
size
はエフェクトで使用する要素のサイズです。遷移中もremember
で常に追跡します。 -
startOffsetX
では、エフェクトの横方向のオフセットを制御しています。Jetpack Composeで無限アニメーションを扱うときはrememberInfiniteTransition
を使うといいでしょう。
https://developer.android.com/jetpack/compose/animation/value-based#rememberinfinitetransition- 開始位置と終了位置はそれぞれ要素の左外側と右外側に適当に配置しています(size.width.toFloat()に -1.5 ~ -2 を掛けるくらいが妥当かもしれません)
-
linearGradient
はなんのアニメーションがどう遷移するかを示しています。エフェクトは斜めに光らせたいのでlinearGradient
を使用しています。開始地点は左上、終了地点は要素の右下を設定することで左上から右下にエフェクトが動作しています。-
startOffsetX
の変化に従って、グラデーションが要素内を移動し、キラっと光るエフェクトを作り出しています。
-
-
background(linearGradient).onGloballyPositioned { size = it.size }
では、エフェクトの遷移によって更新される要素のサイズを取得してsize
に代入しています
動作例
試しに次のような適当なLazyColumnでShimmer Effectを走らせてみます
LazyColumn{
items(10){
Row(
modifier = Modifier.padding(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Box(modifier = Modifier
.padding(start = 8.dp)
.size(72.dp)
.clip(CircleShape)
.shimmer())
Column(modifier = Modifier.padding(start = 16.dp)) {
Box(modifier = Modifier.width(180.dp).height(20.dp).shimmer())
Box(modifier = Modifier.padding(top = 8.dp).width(80.dp).height(30.dp).shimmer())
}
}
}
}
すると次のように動作します
おわりに
でも実際はライブラリを使ったほうがいいよねってはなし
よさげなShimmerのライブラリですどうぞ↓
Discussion