Files
coco 723ce1af5c a
2026-07-03 15:12:48 +08:00

101 lines
3.1 KiB
Markdown

# Modifier.zoomable()
A `Modifier` for handling pan & zoom gestures, designed to be shared across all your media composables so that your users can use the same familiar gestures throughout your app. It offers,
- Pinch to zoom and flings
- Double tap to zoom
- Single finger zoom (double tap and hold)
- Haptic feedback for over/under zoom
- Compatibility with nested scrolling
- Click listeners
### Installation
```groovy
implementation("me.saket.telephoto:zoomable:{{ versions.telephoto }}")
```
```kotlin hl_lines="4"
Box(
Modifier
.size(200.dp)
.zoomable(rememberZoomableState())
.background(
brush = Brush.linearGradient(listOf(Color.Cyan, Color.Blue)),
shape = RoundedCornerShape(4.dp)
)
)
```
While `Modifier.zoomable()` was primarily written with images & videos in mind, it can be used for anything such as text, canvas drawings, etc.
### Edge detection
| ![type:video](../assets/edge_detection_before.mp4) | ![type:video](../assets/edge_detection_after.mp4) |
|:--------------------------------------------------:|:-------------------------------------------------:|
| Without edge detection | With edge detection |
For preventing your content from over-zooming or over-panning, `Modifier.zoomable()` will use your content's layout size by default. This is good enough for composables that fill _every_ pixel of their drawing space.
For richer content such as an `Image()` whose _visual_ size may not always match its layout size, `Modifier.zoomable()` will need your assistance.
```kotlin hl_lines="5-7"
val state = rememberZoomableState()
val painter = resourcePainter(R.drawable.example)
LaunchedEffect(painter.intrinsicSize) {
state.setContentLocation(
ZoomableContentLocation.scaledInsideAndCenterAligned(painter.intrinsicSize)
)
}
Image(
modifier = Modifier
.fillMaxSize()
.background(Color.Orange)
.zoomable(state),
painter = painter,
contentDescription = …,
contentScale = ContentScale.Inside,
alignment = Alignment.Center,
)
```
### Click listeners
For detecting double taps, `Modifier.zoomable()` consumes all tap gestures making it incompatible with `Modifier.clickable()` and `Modifier.combinedClickable()`. As an alternative, its `onClick` and `onLongClick` parameters can be used.
```kotlin
Modifier.zoomable(
state = rememberZoomableState(),
onClick = { … },
onLongClick = { … },
)
```
### Applying gesture transformations
When pan & zoom gestures are received, `Modifier.zoomable()` automatically applies their resulting `scale` and `translation` onto your content using `Modifier.graphicsLayer()`.
This can be disabled if your content prefers applying the transformations in a bespoke manner.
```kotlin hl_lines="2 10-11"
val state = rememberZoomableState(
autoApplyTransformations = false
)
Text(
modifier = Modifier
.fillMaxSize()
.zoomable(state),
text = "Nicolas Cage",
style = state.contentTransformation.let {
val weightMultiplier = if (it.isUnspecified) 1f else it.scale.scaleX
TextStyle(
fontSize = 36.sp,
fontWeight = FontWeight(400 * weightMultiplier),
)
}
)
```