WhatsApp’s Round Reveal with Jetpack Compose
[ad_1]
On this article, I’ll present you how one can construct the round reveal animation WhatsApp makes use of with Jetpack Compose.
My first try was to make use of AnimatedVisibility
to realize one thing related.
AnimatedVisibility(
seen = seen,
) {
BottomSheet()
}
This code is equal to:
AnimatedVisibility(
seen = seen,
enter = expandVertically(),
exit = shrinkVertically()
) {
BottomSheet()
}
Nevertheless, I used to be not in a position to obtain the round movement I used to be in search of.
My second try was to make use of a form to clip my composable to. GenericShape
permits me to write down no matter form I would like however there’s nothing that permits me to attract a circle so I began with a rectangle.
GenericShape { measurement, path ->
val heart = measurement.width / 2f
this.addRect(
Rect(
left = heart - heart * it,
prime = heart - heart * it,
proper = heart + heart * it,
backside = measurement.peak
)
) shut()
}
I received one thing to animate but it surely nonetheless missed the round movement I used to be in search of.
Given {that a} rectangle will not be what I would like I attempted utilizing addArc
to attract an arc across the rectangle.
GenericShape { measurement, path ->
val centerV = measurement.peak / 2f
val centerH = measurement.width / 2f
addArc(
Rect(
left = centerH - centerH * it * 2,
prime = measurement.peak - measurement.peak * it,
proper = centerH + centerH * it * 2,
backside = measurement.peak + measurement.peak * it
),
0f,
-180f
) shut()
}
The end result was a lot better, nonetheless, I couldn’t get it to broaden totally so there was all the time part of the element that was cropped out.
What if as a substitute of utilizing a standard arc I used a extra “difficult” one? That’s once I tried to make use of cubicTo
, it makes use of a cubic bézier curve. It has 4 management factors permitting me to attract one thing that appears like semicircle.
If I applied a cubic bézier curve that has the identical measurement as my composable then the highest corners would nonetheless be cropped out so I wanted a method to make the curve wrap my complete composable.
To do this I made the curve twice as large because the composable. For the x
axis that’s finished by multiplying the width by 2, for the y
axis I’m utilizing a lerp
operate that goes to from peak
to -height
.
GenericShape { measurement, path ->
val centerH = measurement.width / 2f
moveTo(
x = centerH - centerH * it * 2,
y = measurement.peak,
) val currentHeight = lerp(measurement.peak, -size.peak, it) cubicTo(
x1 = centerH - centerH * it,
y1 = currentHeight,
x2 = centerH + centerH * it,
y2 = currentHeight,
x3 = centerH + centerH * it * 2,
y3 = measurement.peak,
) shut()
}
The end result was fairly near what I needed however the form will not be rising proportionally.
I stored tweaking the code till I arrived at one thing that was fairly near the animation WhatsApp is utilizing.
GenericShape { measurement, _ ->
val centerH = measurement.width / 2f
val multiplierW = 1.5f + measurement.peak / measurement.width
moveTo(
x = centerH - centerH * progress * multiplierW,
y = measurement.peak,
) val currentWidth = (centerH * progress * multiplierW * 2.5f) cubicTo(
x1 = centerH - centerH * progress * 1.5f,
y1 = measurement.peak - currentWidth * 0.5f,
x2 = centerH + centerH * progress * 1.5f,
y2 = measurement.peak - currentWidth * 0.5f,
x3 = centerH + centerH * progress * multiplierW,
y3 = measurement.peak,
) shut()
}
There are some magic numbers within the calculations and I’m undecided why they work, nonetheless, the top end result appears to be like fairly just like what WhatsApp is utilizing.
Should you check out the WhatsApp animation once more you would possibly discover that the objects inside the cardboard additionally animate a bit. Their scale at first might be 90%, then it goes as much as 110% and at last goes right down to 100%.
To implement 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’s a easy float animation that goes from 0.9 to 1 and makes use of a spring animation spec. I’m including a 20ms delay + one other delay based mostly on the merchandise place so the whole lot doesn’t animate on the similar time. If you would like one thing to look later then simply give it the next place.
It doesn’t look precisely just like the one WhatsApp makes use of however if you happen to can proceed tweaking the delay
and the dampingRatio
worth till you get one thing you’re pleased with.
If you wish to test the supply code, you will discover it here.
If in case you have any feedback or options, please attain out to me on Twitter.
[ad_2]