Skip to content

Breathing Scale

During a pinch gesture, the grid subtly scales up (zooming in) or down (zooming out) following your fingers. This provides real-time visual feedback before the column count snaps.

How it works

The breathing effect uses graphicsLayer — zero recompositions, pure GPU transform at 60fps. During the gesture, scale tracks scaleProgress instantly (0ms tween). On release, it animates back to 1.0 over 150ms.

Custom per-item transforms

You can use state.scaleProgress and state.isZoomingIn to apply custom transforms to individual items:

items(photos, key = { it.id }) { photo ->
val itemScale = when (state.isZoomingIn) {
true -> 1f + (state.scaleProgress * 0.1f)
false -> 1f - (state.scaleProgress * 0.1f)
null -> 1f
}
AsyncImage(
model = photo.url,
modifier = Modifier
.graphicsLayer { scaleX = itemScale; scaleY = itemScale }
.aspectRatio(1f),
)
}

State properties

PropertyTypeDescription
scaleProgressFloat0f–1f, how close to the next column snap
isZoomingInBoolean?true = spreading fingers, false = pinching, null = idle