Reputation: 13
How can I create an animation like this to represent objects that are loading, thanks to everyone. Shimmer example
I looked for the whole day but the information that I found was in views, if anyone knows a dependency or how to create this I will be thankful.
Upvotes: 1
Views: 1934
Reputation: 339
The shimmer code works well, but it has a major recomposition issue. Here, I’ve optimized it using drawBehind to improve performance.
@Composable
fun Modifier.shimmerLoading(
durationMillis: Int = 1000,
): Modifier {
val transition = rememberInfiniteTransition(label = "")
val translateAnimation by transition.animateFloat(
initialValue = 0f,
targetValue = 500f,
animationSpec = infiniteRepeatable(
animation = tween(
durationMillis = durationMillis,
easing = LinearEasing,
),
repeatMode = RepeatMode.Restart,
),
label = "",
)
return drawBehind {
drawRect(
brush = Brush.linearGradient(
colors = persistentListOf(
Color.LightGray.copy(alpha = 0.2f),
Color.LightGray.copy(alpha = 1.0f),
Color.LightGray.copy(alpha = 0.2f),
),
start = Offset(x = translateAnimation, y = translateAnimation),
end = Offset(x = translateAnimation + 100f, y = translateAnimation + 100f),
)
)
}
}
Using this is straightforward — just call it to the Modifier of your composable.
Box(
modifier = Modifier
.size(80.dp)
.clip(CircleShape)
.shimmerLoading()
)
I highly recommend checking out this Medium article titled: "Shimmer Animation in Jetpack Compose Without Recomposition" about creating a shimmer effect with no recomposition and optimized performance. It’s packed with great tips!
Upvotes: -1
Reputation: 89
Accompanist placeholder api is deprecated. You can use the following structure instead.
@Composable
fun ShimmerEffectBox(
modifier: Modifier = Modifier, isShow: Boolean = true,
content: @Composable BoxScope.() -> Unit
) {
val shimmerColors = listOf(
Color.LightGray.copy(alpha = 0.6f),
Color.LightGray.copy(alpha = 0.2f),
Color.LightGray.copy(alpha = 0.6f)
)
val transition = rememberInfiniteTransition(label = "")
val translateAnim by transition.animateFloat(
initialValue = 0f, targetValue = 1000f, animationSpec = infiniteRepeatable(
animation = tween(durationMillis = 1700, delayMillis = 200),
repeatMode = RepeatMode.Restart
), label = ""
)
val brush = Brush.linearGradient(
colors = shimmerColors,
start = Offset.Zero,
end = Offset(x = translateAnim, y = translateAnim)
)
if (isShow) {
Box(
modifier = modifier.background(brush)
) {
Box(modifier = Modifier
.matchParentSize()
.graphicsLayer { alpha = 1f })
}
} else {
Box(
modifier = modifier
) {
content()
}
}
}
Usage:
Row {
ShimmerEffectBox(
modifier = Modifier
.height(100.dp)
.padding(4.dp)
.weight(0.3f)
.clip(RoundedCornerShape(4.dp)),
isShow = true
) {
Image(painter = painterResource(id = R.drawable.ic_launcher_background), contentDescription = "")
}
ShimmerEffectBox(
modifier = Modifier
.height(100.dp)
.padding(4.dp)
.weight(0.7f)
.clip(RoundedCornerShape(4.dp)),
isShow = true
) {
Text(
text = "Content to display after content has loaded"
)
}
}
Upvotes: 0
Reputation: 13
you can use rememberInfiniteTransition
, for example simple shimmer animation below
val infiniteTransition = rememberInfiniteTransition(label = "")
val skeletonAlpha by infiniteTransition.animateFloat(
// between these states, the skeleton will flicker
initialValue = 1f, // skeleton max alpha
targetValue = 0.5f, // skeleton min alpha
animationSpec = infiniteRepeatable(
// every 500ms, but you can change this time
animation = tween(500, easing = LinearEasing),
repeatMode = RepeatMode.Reverse
), label = ""
)
Card(modifier = Modifier.alpha(skeletonAlpha))
Upvotes: 0