On this article, I’ll present you the way to create a round reveal animation that WhatsApp makes use of with Jetpack Compose.
My first try was to make use of AnimatedVisibility
to attain one thing related.
AnimatedVisibility(
seen = seen,
) {
BottomSheet()
}
This code is equal to:
AnimatedVisibility(
seen = seen,
enter = expandVertically(),
exit = shrinkVertically()
) {
BottomSheet()
}
Nevertheless, I’m not in a position to obtain the round movement I’m on the lookout for.
My second try was to make use of a form to chop my composable issues. GenericShape
permits me to write down any form I need however nothing permits me to attract a circle so I begin with a rectangle.
GenericShape { measurement, route ->
val heart = measurement.width / 2f
this.addRect(
Rect(
left = heart - heart * it,
high = heart - heart * it,
proper = heart + heart * it,
backside = measurement.top
)
) shut()
}
I’ve one thing to animate but it surely nonetheless misses the round movement I am on the lookout for.
Given {that a} rectangle isn’t what I need, I attempted utilizing addArc
to attract an arc across the rectangle.
GenericShape { measurement, route ->
val centerV = measurement.top / 2f
val centerH = measurement.width / 2f
addArc(
Rect(
left = centerH - centerH * it * 2,
high = measurement.top - measurement.top * it,
proper = centerH + centerH * it * 2,
backside = measurement.top + measurement.top * it
),
0f,
-180f
) shut()
}
The result’s significantly better, nonetheless, I am unable to scale it utterly so there’s at all times a part of the element reduce out.
What if as an alternative of utilizing a standard arc, I exploit a extra “complicated” arc? That is once I tried to make use of cubicTo
, it makes use of a cubic Bézier curve. It has 4 management factors that enable me to attract one thing like a semicircle.
If I implement a Bézier dice curve of the identical measurement as my composable curve the highest corners will nonetheless be reduce out so I want a solution to make the curve wrap the entire line my mixable curvy.
To try this, I made the curve twice as massive because the recombinable curve. Give x
axis is finished by multiplying the width by 2, for y
i’m utilizing an axis lerp
perform come from top
ARRIVE -height
.
GenericShape { measurement, route ->
val centerH = measurement.width / 2f
moveTo(
x = centerH - centerH * it * 2,
y = measurement.top,
) val currentHeight = lerp(measurement.top, -size.top, it) cubicTo(
x1 = centerH - centerH * it,
y1 = currentHeight,
x2 = centerH + centerH * it,
y2 = currentHeight,
x3 = centerH + centerH * it * 2,
y3 = measurement.top,
) shut()
}
The result’s fairly near what I need however the form does not develop proportionally.
I stored tweaking the code till I discovered one thing fairly near the animation WhatsApp was utilizing.
GenericShape { measurement, _ ->
val centerH = measurement.width / 2f
val multiplierW = 1.5f + measurement.top / measurement.width
moveTo(
x = centerH - centerH * progress * multiplierW,
y = measurement.top,
) val currentWidth = (centerH * progress * multiplierW * 2.5f) cubicTo(
x1 = centerH - centerH * progress * 1.5f,
y1 = measurement.top - currentWidth * 0.5f,
x2 = centerH + centerH * progress * 1.5f,
y2 = measurement.top - currentWidth * 0.5f,
x3 = centerH + centerH * progress * multiplierW,
y3 = measurement.top,
) shut()
}
There are some magic numbers within the calculation and I am undecided why they work, nonetheless, the top consequence seems to be fairly just like what WhatsApp is utilizing.
In the event you assessment the WhatsApp animations, it’s possible you’ll discover that the objects inside the cardboard additionally animate a bit. Their scale could be at first 90%, then enhance to 110% and at last lower to 100%.
For implementation that I used animateFloatAsState
.
var scale by bear in mind { mutableStateOf(0.9f) }
val animation = animateFloatAsState(
targetValue = scale,
animationSpec = FloatSpringSpec(
dampingRatio = 0.3f,
)
)
LaunchedEffect(Unit) {
delay(20 + place.toLong() * 20)
scale = 1f
}Picture(
modifier = Modifier
...
.graphicsLayer {
scaleX = animation.worth
scaleY = animation.worth
}
)
It is a easy float animation that goes from 0.9 to 1 and makes use of the spring animation spec. I am including a 20 millisecond delay + one other delay based mostly on merchandise place so every part does not animate on the identical time. If you would like one thing to look later, simply give it a better place.
It does not look precisely like what WhatsApp makes use of however in case you can maintain tweaking delay
and dampingRatio
worth till you get one thing that you’re happy with.
If you wish to verify the supply code, you will discover it This.
In case you have any feedback or recommendations, be happy to contact me on Twitter.