Understanding and Customizing Easing in Android Compose Animations
This article explains the concept of Easing in Android Compose, shows how to use built‑in Easing functions, lists the available Easing types, and demonstrates how to create custom Easing implementations with Kotlin code examples and visual demos.
What is Easing?
Easing is a function used with time‑based AnimationSpec operations such as tween or keyframes to adjust the animation's fractional progress, allowing the value to accelerate or decelerate instead of moving at a constant rate. The function receives a fraction between 0 and 1 and returns a float, which may lie outside the range to produce overshoot or undershoot. In Compose, Easing works similarly to the platform Interpolator but uses the transform() method instead of getInterpolation() .
How to Use Easing?
Typical usage involves passing an Easing instance to the easing parameter of a tween specification:
val value1 by animateFloatAsState(
targetValue = 1f,
animationSpec = tween(
durationMillis = 300,
delayMillis = 50,
easing = LinearOutSlowInEasing // using Easing
)
)Selecting the appropriate Easing determines the animation’s feel.
Which Easing Implementations Exist?
Compose provides several ready‑made Easing objects, all subclasses of CubicBezierEasing :
FastOutSlowInEasing
LinearOutSlowInEasing
FastOutLinearEasing
LinearEasing
CubicBezierEasing
Each corresponds to a distinct animation curve; the official documentation lists many more.
How to Customize Easing?
The core class is CubicBezierEasing , which implements Easing by evaluating a cubic Bézier curve. Its constructor takes four Float parameters (a, b, c, d) representing the control points of the curve.
@Immutable
class CubicBezierEasing(
private val a: Float,
private val b: Float,
private val c: Float,
private val d: Float
) : Easing {
// ...
private fun evaluateCubic(a: Float, b: Float, m: Float): Float {
return 3 * a * (1 - m) * (1 - m) * m +
3 * b * (1 - m) * m * m +
m * m * m
}
override fun transform(fraction: Float): Float {
if (fraction > 0f && fraction < 1f) {
var start = 0.0f
var end = 1.0f
while (true) {
val midpoint = (start + end) / 2
val estimate = evaluateCubic(a, c, midpoint)
if ((fraction - estimate).absoluteValue < CubicErrorBound) {
return evaluateCubic(b, d, midpoint)
}
if (estimate < fraction) start = midpoint else end = midpoint
}
} else {
return fraction
}
}
// ...
}For simple cases you can implement Easing directly, e.g., a linear easing:
val LinearEasing: Easing = Easing { fraction -> fraction }Or create a custom easing that halves the progress:
val CustomLinearEasing: Easing = Easing { fraction -> fraction / 2 }Using the custom easing in an animation:
var small by remember { mutableStateOf(true) }
val size by animateDpAsState(
targetValue = if (small) 50.dp else 100.dp,
animationSpec = tween(
durationMillis = 3000,
delayMillis = 50,
easing = CustomLinearEasing // custom Easing
)
)
Column {
Button(onClick = { small = !small }) { Text("Modify Size") }
Box(modifier = Modifier.size(size))
}Changing the easing function (e.g., returning -fraction/2 or fraction*2 ) dramatically alters the animation timing, as demonstrated by the accompanying GIFs.
Summary
Easing is essential for fine‑tuning Compose animations. The built‑in Easing objects cover most needs, but when a specific curve is required you can either implement Easing directly or extend CubicBezierEasing with custom control‑point values.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.