DevBlogs
May 18th, 2023

# Blooming love for Compose animation

SW/FW Engineer

Hello Jetpack Compose developers,

Today we’ll be continuing our blog series on animations in Jetpack Compose! This content was inspired by Nicole Terc’s Composable Sheep talk from droidcon NYC.

## More basic animations

We’ll be continuing to use our `drawSunflower` method and `Sketch` composable to build animations in this post, so if you haven’t already, check out last week’s blog post to see how we built those functions!

Last week, we took a look at implementing a basic size animation for our sunflower garden.

Today, we’ll learn how to do animations with two other properties: angle and color.

### Rotation animation

To add some rotation to our garden, we’ll first have to revisit our flower drawing code. This time, our animation state will need to be float that varies between `0f` and `360f`, to represent the angle offset. To incorporate the angle into our drawing code, we only need to make one simple change:

```  fun DrawScope.drawSunflower(sizePct: Float = 1f, rotation: Float = 0f) {
drawPetals(sizePct, rotation)
drawCenter(sizePct)
}

// drawCenter remains the same

fun DrawScope.drawPetals(sizePct: Float, rotation: Float) {
val numPetals = 8
var angle = rotation
val size = Size(width = 30f, height = 75f) * sizePct

repeat(numPetals) {        rotate(angle) {            drawOval(
color = Yellow,
topLeft = Offset(center.x - size.width / 2, center.y),
size = size
)
}        angle += 360 / numPetals
}
}```

Instead of starting the `angle` variable at `0f` when making calculations for where the petal ovals will be drawn, we simply start at the current angle offset in the rotation. The `rotation` angle only needs to be passed into the `drawPetal` function, so the `drawCenter` function can remain the same as before.

To set up the animation behavior, let’s remind ourselves of what the `Sketch` composable and animation spec methods `infiniteRepeatable` and `tween` look like:

```  @Composable
fun Sketch(
modifier: Modifier = Modifier,
targetValue: Float,
animationSpec: AnimationSpec<Float>,
onDraw: DrawScope.(Float) -> Unit
)

@Stable
fun <T> infiniteRepeatable(
animation: DurationBasedAnimationSpec<T>,
repeatMode: RepeatMode = RepeatMode.Restart,
initialStartOffset: StartOffset = StartOffset(0)
): InfiniteRepeatableSpec<T>

@Stable
fun <T> tween(
durationMillis: Int = DefaultDurationMillis,
delayMillis: Int = 0,
easing: Easing = FastOutSlowInEasing
): TweenSpec<T>```

We already noted that our animation state should vary between `0f` and `360f` to cover every angle, so we’ll set the `targetValue` to `360f`.

For the animation spec, I want my sunflowers to rotate at a constant speed infinitely. To make the rotation look seamless as it repeats, we’ll leave the `repeatMode` and `delayMillis` parameters at their default values. And to achieve a constant rotation speed, this time we’ll use a `LinearEasing` so the angle changes at a constant rate.

Putting all that together, we can write the `AnimatedRotationSunflower` composable:

```  @Composable
fun AnimatedRotationSunflower() {
Sketch(
modifier = Modifier.size(100.dp),
targetValue = 360f,
animationSpec = infiniteRepeatable(tween(durationMillis = 2000, easing = LinearEasing))
) { angle ->
drawSunflower(rotation = angle)
}
}```

Look at our garden now!

### Color animation

As beautiful as the sunflowers are in yellow and brown, I think the garden could benefit from some additional colors! Let’s try to add some color-based animations.

Thanks to the composable sheep talk, we know that `Color.hsv` method is a great way to animate color based on angle, since the `hue` parameter must be in the range of `0f-360f`.

To update our drawing code to accept our animated color, we only have to pass in the color parameter and use it when drawing our petal ovals:

```  fun DrawScope.drawSunflower(sizePct: Float = 1f, rotation: Float = 0f, color: Color = Yellow) {
drawPetals(sizePct, rotation, color)
drawCenter(sizePct)
}

// drawCenter remains the same

fun DrawScope.drawPetals(sizePct: Float, rotation: Float, color: Color) {
val numPetals = 8
var angle = rotation
val size = Size(width = 30f, height = 75f) * sizePct

repeat(numPetals) {
rotate(angle) {
drawOval(
color = color,
topLeft = Offset(center.x - size.width / 2, center.y),
size = size
)
}
angle += 360 / numPetals
}
}```

Super simple! As for our animation, I want very similar behavior to our rotation animation from above – seamless infinite looping, constant rate of change – so our final composable will look like this:

```  @Composable
fun AnimatedColorSunflower() {
Sketch(
modifier = Modifier.size(100.dp),
targetValue = 360f,
animationSpec = infiniteRepeatable(tween(durationMillis = 3000, easing = LinearEasing))
) { hue ->
drawSunflower(color = Color.hsv(hue = hue, saturation = 1f, value = 1f))
}
}```

As previously mentioned, the `Color.hsv` method accepts a hue between `0f` and `360f`, so we set our `targetValue` to `360f` and pass in the animated value to generate a new color.

Here’s the result:

Pretty cool, but I think we can do even better! It would be awesome to have each individual petal change color over time for a sort of rainbow spinner effect. To do this, we’ll mostly have to update the drawing code so that each petal is drawn with a slightly different color. We’ll keep all the animation spec details the same, but instead of passing in one color for all the petals, we’ll now just pass in the hue and do calculations while drawing:

```  @Composable
fun AnimatedPetalColorSunflower() {
Sketch(
modifier = Modifier.size(100.dp),
targetValue = 360f,
animationSpec = infiniteRepeatable(tween(durationMillis = 3000, easing = LinearEasing))
) { hue ->
drawSunflower(hue = hue)
}
}

fun DrawScope.drawSunflower(sizePct: Float = 1f, rotation: Float = 0f, color: Color = Yellow, hue: Float? = null) {
drawPetals(sizePct, rotation, color, hue)
drawCenter(sizePct)
}

// drawCenter remains the same

fun DrawScope.drawPetals(sizePct: Float, rotation: Float, color: Color, hue: Float?) {
val numPetals = 8
var angle = rotation
val size = Size(width = 30f, height = 75f) * sizePct

repeat(numPetals) { petal ->
rotate(angle) {
drawOval(
color = hue?.let { Color.hsv(hue = (hue + petal * 10f).mod(360f), saturation = 1f, value = 1f) }
?: color,
topLeft = Offset(center.x - size.width / 2, center.y),
size = size
)
}
angle += 360 / numPetals
}
}```

You can see that we now call `Color.hsv` in `drawPetals`, and we use the current petal number to offset the hue value. Because we are adding to the hue, we do have to call `mod(360f)` to make sure the final hue value is within the required range.

And here’s the final result – look at how much cooler our garden is now:

And that’s it for the additional basic animations we’ll cover today. The possibilities really are endless – you could also animate the number of petals, the position of the flower, or any combination of the properties we already animated. Next week, we’ll finish up this blog series by finalizing our flower varieties and adding some green 🟩!

## Resources and feedback

The content covered today is part of PR #2 in the animated-garden repo.

If you have any questions, use the feedback forum or message us on Twitter @surfaceduodev.

We won’t be livestreaming this week, but you can check out the archives on YouTube.

## Author

SW/FW Engineer

Works in the Surface Duo Developer Experience team to help with all aspects of dual-screen SDK development and customer engagement.