Skip to content
Kapow
Esc
↑↓navigate↵open⌘Jpreview
On this page

Change: ripple

An ink ripple spreads across an element, inside its shape, when a value changes.

Ripple spreads a circle of ink from a point on the element until it covers it, then lets it fade. It stays inside the element’s shape. Use it to confirm that something on the element changed state, such as a task that was ticked off or a row that was selected.

Usage

The sample sends a ripple from the checkbox side of a to-do row each time the task is ticked or unticked.

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.unit.dp
import com.jantursky.kapow.ChangeEffect
import com.jantursky.kapow.changeEffect

@Composable
fun TaskRow(done: Boolean) {
    val shape = RoundedCornerShape(16.dp)
    Box(
        Modifier
            .size(width = 260.dp, height = 64.dp)
            .changeEffect(
                value = done,
                effect = ChangeEffect.ripple(
                    color = Color(0x593D7BFF),
                    shape = shape,
                    origin = TransformOrigin(0.1f, 0.5f),
                ),
            )
            .clip(shape)
            .background(Color(0xFF161616)),
    )
}

Parameters

Parameter Default Description
color Color(0x66FFFFFF) Color of the ink. Its alpha sets how strong the ripple is; the default is white at 40 %.
shape RectangleShape The element’s shape. The ripple is clipped to it, so pass the same shape you clip or draw the element with.
origin TransformOrigin.Center Where the ripple starts, as fractions of the element’s width and height. TransformOrigin(0f, 0f) is the top left corner.
duration 600.milliseconds How long the ripple takes to spread and fade. Must be positive.
rings 1 How many rings spread, one after another. Must be positive.
ringWidth Dp.Unspecified The width of each ring. Unspecified draws each ring as a filled disc, like ink.
maxRadius Dp.Unspecified How far the rings spread. Unspecified is as far as the element’s farthest corner.
ringSpacing 0.2f How long after the one before each ring starts, as a fraction of duration. The rings still end together, so (rings - 1) * ringSpacing must stay below 1.
easing LinearEasing The curve of the ripple’s life. The spread itself always slows towards the end.

Reduced motion

Nothing spreads. The whole shape tints in color and fades back in 0.3 s. With reducedMotion(ReducedMotionMode.EndState) nothing shows at all, since the effect ends at rest.

Notes

  • The circle grows until it reaches the corner farthest from origin, quickly at first and then slower. It stays at full strength for the first 40 % of duration, then fades out.
  • The ink is drawn on top of the element’s content, so pick a translucent color.
  • A new change while a ripple runs starts a fresh one from the origin. There is no cooldown.
  • Nothing extra is drawn or allocated at rest. No blur or shader is involved.

Live example

Was this page helpful?