Animation In Compose Series (Part 2) — Understanding AnimationSpec

Bharat Kumar
5 min readMar 13, 2024

--

Another Article in my Series of Animations in Compose. Do check out the previous Article on Animated Visibility and Animated Content.

Here in this Article we will understand in depth about AnimationSpec clearly and see how to use it as there are many functions and composable which require AnimationSpec as a parameter.

So let's see what are those.

There are 2 predefined AnimationSpec

  1. Tween
  2. Spring

Tween

Tween is a simple animation that works on easing. In simple words, tween works in this way.

Let’s say we are animating a line with animationSpec as a tween it will move from one point to another in a linear way depending on the Easing given.

Easing allows value to speed up and slow down, rather than moving at a constant.

As you can see here different Easing have different values. They are the same as the Animation Interpolators we had in XML animations.
You can also create your own Easing by using the CubicBezierEasing class.

Example of how to use Tween

@Composable
fun TweenExample() {
var isEnabled by remember {
mutableStateOf(false)
}
val pathAnimation = animateFloatAsState(
targetValue = if (!isEnabled) 0f else 1f,
animationSpec = tween(durationMillis = 300, easing = FastOutSlowInEasing),
label = "Tween FastOutSlowInEasing Animation"
)
}

Spring

Spring is a Physics-based animation that just works like it says “Like a Spring”. it gives a bouncy animation depending upon 2 things specified one is DampingRatio and the other is Stiffness.

Damping Ratio — It defines how bouncy the animation should be.
Stiffness — This defines how fast the animation should move toward the end value.

Here is how it looks with different dampingRatio

Yes this I copied from Android Docs cos its good

The default dampingRatio for spring is Spring.DampingRatioNoBouncy

Here is how it looks with different stiffness

Yes I Did it again

Here is how you would use it.

@Composable
fun SpringExample() {
var isEnabled by remember {
mutableStateOf(false)
}
val pathAnimation = animateFloatAsState(
targetValue = if (!isEnabled) 0f else 1f,
animationSpec = spring(
dampingRatio = Spring.DampingRatioHighBouncy,
stiffness = Spring.StiffnessLow
),
label = "Tween FastOutSlowInEasing Animation",
)

LaunchedEffect(key1 = Unit) {
isEnabled = true
}
Canvas(modifier = Modifier.fillMaxSize()) {
drawCircle(
color = Color.Black,
radius = 90f,
center = Offset((pathAnimation.value * 600f) + 100f, 100f)
)
}
}
Did you think I would only copy?

Now these were some predefined AnimationSpec but how to use more than that.
That’s when Key Frames comes to act.

KeyFrames

keyframes simply means we can animate based on the values at different timestamps. You can also specify the easing on a particular time stamp.

Here is how you would use it.

@Composable
fun KeyframeExample() {
var isEnabled by remember {
mutableStateOf(false)
}
val pathAnimation = animateFloatAsState(
targetValue = if (!isEnabled) 0f else 1f,
animationSpec = keyframes {
//total duration
durationMillis = 5000
0.0f at 0 with LinearOutSlowInEasing// for 0-15 ms
0.2f at 1000 // for 15-75 ms
0.5f at 2500 // ms
0.7f at 4000 with EaseInBounce// ms
},
label = "KeyframeExample",
)
LaunchedEffect(key1 = Unit) {
isEnabled = true
}
Canvas(modifier = Modifier.fillMaxSize()) {
drawCircle(
color = Color.Black,
radius = 40f,
center = Offset((pathAnimation.value * 700f) + 100f, 100f)
)
}
}

Here if you see the map

keyframes {
//total duration
durationMillis = 5000
0.0f at 0 with LinearOutSlowInEasing// for 0-15 ms
0.2f at 1000 // for 1000-2500 ms
0.5f at 2500 // ms
0.7f at 4000 with EaseInBounce// ms
}

durationMills is the total duration of the Animation

The float value at the start 0.2f at 1000 means that from 1000 ms to 2500 ms, the animation will cover 20 percent of the total value as per the above example as the target value is from 0 to 1.

From 1000 ms to 2000 ms the animation will cover the value from 0.2 to 0.5

The added advantage here is you can specify the easing with that like we did below

0.7f at 4000 with EaseInBounce
This is how the keyframe code will look

As you can see above at the last movement the circle is doing a bounce animation that is due to the last part as we added EaseInBounce animation at the last keyframe.

Some more tips at the end.

Wherever there is a requirement for an AnimationSpec for animation here is how you could do Infinite animations.

Infinite Animations from AnimationSpec

val value by animateFloatAsState(
targetValue = 1f,
animationSpec = infiniteRepeatable(
animation = tween(durationMillis = 2000),
repeatMode = RepeatMode.Reverse
)
)

Repeatable Animations from AnimationSpec

val value by animateFloatAsState(
targetValue = 1f,
animationSpec = repeatable(
iterations = 10,
animation = tween(durationMillis = 1000),
repeatMode = RepeatMode.Reverse
)
)

Follow me and Stay tuned for more such content. Feedback would be appreciated as always.

A clap also would be appreciated but a read would be enough.

Thank You and have a Happy coding Journey. :)

You can connect with me at LinkedIn — Bharat Kumar

Here is the GitHub repo with samples of Animation — https://github.com/PalankiBharat/Compose-Animation-Samples.git

--

--

Bharat Kumar
Bharat Kumar

Written by Bharat Kumar

Indian Android Developer || Kotlin Enthusiast

Responses (1)