The Art of Swing Effect With Jetpack Compose

Learn how to rotate things with an anchor point

Stephen Vinouze
Better Programming

--

Photo by Remy_Loz on Unsplash

Compose has come a long way to challenge how to build declarative UI. And animating objects don’t fall short either. The API offers a unified toolkit to let you polish your effects.

Having said that, rediscovering a new API can be frustrating. You’ll bump into difficulties while trying to reproduce an effect you knew how to perform with the former animation API. That’s what happened to me while trying to swing an object like a pendulum. Here is what I wanted to achieve:

The swing effect

At first sight, the animation looks like an infinite back and forth rotation from one angle to another. Nothing extraordinary, right? Let’s see how it goes!

Building The Composable

Before achieving the above effect, let’s build the composable we’ll want to swing. For the sake of example, I’ll not dwell on the arrow nor on the inner part of the rotated shape.

For this composable, we need two things: the shape we’ll rotate and the nail anchoring it. Given its simplicity, we can use a Surface for both components — drawing on a Canvas would be perfectly fine too. We can encapsulate both Panel and Nail composables within a Box with a TopCenter alignment to place the nail.

Swing composable

The result should look like this:

To rotate our Panel composable, we must provide a dynamic angle. Since we want the shape to swing indefinitely, we can leverage the transition API with rememberInfiniteTransition . Then, we update the angle by changing its value from a starting point angleOffset to its final destination — I’ve used its negative counterpart for symmetry purposes but you could choose otherwise.

For the animation part itself, I’ve used a tween interpolation. Compose comes with many built-in animations to achieve the desired effect. I strongly suggest you take a look at their well-covered documentation.

Finally, we apply this angle to the Panel’s Modifier using the rotate method. Here is the updated Panel ’s code:

And its output:

Panel rotating on itself

Not really what we were expecting, right? The nail doesn’t look like he’s doing a proper job at pining the panel. There is a simple reason behind this: the rotation is anchored at the center of the panel. We need to shift its origin to its anchoring point: the nail.

Shifting The Transformation Origin

Before Compose, changing the anchoring point of a rotation transformation was no big deal. For instance, RotateAnimation lets you change the pivot directly in its constructor:

public RotateAnimation (
float fromDegrees,
float toDegrees,
float pivotX,
float pivotY
)

With Compose, the rotate method doesn’t let you change its pivot, nor any other seemingly related modifiers. Until you find it: transformOrigin.

It matters first to understand that all of the modifier transformation methods wrap layer operations on a composable. The rotate method is no stranger to that:

@Stable
fun Modifier.rotate(degrees: Float) =
if (degrees != 0f) graphicsLayer(rotationZ = degrees) else this

It modifies the layer’s rotationZ property with the given angle. This hints to us that shifting the pivot would come from modifying a graphicLayer property.

The graphicLayer modifier has a transformOrigin property that defaults to TransformOrigin.Center. If you look closer at its implementation, it takes two attributes: pivotFractionX and pivotFractionY. By changing the origin, we can orient it to our nail location.

Given the nail position, that means center horizontally and nearly at the top of the y-axis. Then we apply our rotation directly on the rotationZ attribute.

It produces the following effect:

Exactly what we wanted to do! What about if the element is already rotated as we could see in the introductory animation.

We’d first need to rotate our Swing composable:

Notice the Box’s alignment has changed to TopStart.

We’ve also adjusted our transformation origin because we’ve changed our referential by rotating the Box.

It gives the following animation:

The Art Of Transformation

Changing the animation pivot doesn’t exclusively cover rotation. All transformations can benefit from this property. You should be aware it exists and its utility. It might come in handy for your future animations.

Rediscovering a new API can be frustrating at times. Be confident, though, that the Compose team has you covered. If you feel something is missing out in Compose, I’d recommend joining the Kotlin Slack Channel. Members support each other and make the community grow. A dedicated room exists for Compose-related topics. It’s there I’ve come to ask for help — credits go to Romain Guy for guiding me towards the solution.

Finally, all of the above should be working with Compose Multiplatform– there is nothing Android-specific in this code.

More articles are on the way! Happy coding!

--

--

✍️ Content creator | 👀 200k Views | 🤖 Keen interest in Android and Jetpack Compose | 🤝 Support me: https://medium.com/@s.vinouze/membership