detectTransformGestures
suspend fun PointerInputScope.detectTransformGestures(
panZoomLock: Boolean = false,
onGesture: (centroid: Offset, pan: Offset, zoom: Float, rotation: Float) -> Unit,
)
A gesture detector for rotation, panning, and zoom. Once touch slop has been reached, the user
can use rotation, panning and zoom gestures. onGesture
will be called when any of the rotation,
zoom or pan occurs, passing the rotation angle in degrees, zoom in scale factor and pan as an
offset in pixels. Each of these changes is a difference between the previous call and the current
gesture. This will consume all position changes after touch slop has been reached. onGesture
will also provide centroid of all the pointers that are down.
If panZoomLock
is true
, rotation is allowed only if touch slop is detected for rotation
before pan or zoom motions. If not, pan and zoom gestures will be detected, but rotation gestures
will not be. If panZoomLock
is false
, once touch slop is reached, all three gestures are
detected.
Example Usage:
Code Examples
DetectTransformGestures
@Composable
fun DetectTransformGestures() {
/**
* Rotates the given offset around the origin by the given angle in degrees.
*
* A positive angle indicates a counterclockwise rotation around the right-handed 2D Cartesian
* coordinate system.
*
* See: [Rotation matrix](https://en.wikipedia.org/wiki/Rotation_matrix)
*/
fun Offset.rotateBy(angle: Float): Offset {
val angleInRadians = angle * (PI / 180)
val cos = cos(angleInRadians)
val sin = sin(angleInRadians)
return Offset((x * cos - y * sin).toFloat(), (x * sin + y * cos).toFloat())
}
var offset by remember { mutableStateOf(Offset.Zero) }
var zoom by remember { mutableStateOf(1f) }
var angle by remember { mutableStateOf(0f) }
Box(
Modifier.pointerInput(Unit) {
detectTransformGestures(
onGesture = { centroid, pan, gestureZoom, gestureRotate ->
val oldScale = zoom
val newScale = zoom * gestureZoom
// For natural zooming and rotating, the centroid of the gesture should
// be the fixed point where zooming and rotating occurs.
// We compute where the centroid was (in the pre-transformed coordinate
// space), and then compute where it will be after this delta.
// We then compute what the new offset should be to keep the centroid
// visually stationary for rotating and zooming, and also apply the pan.
offset =
(offset + centroid / oldScale).rotateBy(gestureRotate) -
(centroid / newScale + pan / oldScale)
zoom = newScale
angle += gestureRotate
}
)
}
.graphicsLayer {
translationX = -offset.x * zoom
translationY = -offset.y * zoom
scaleX = zoom
scaleY = zoom
rotationZ = angle
transformOrigin = TransformOrigin(0f, 0f)
}
.background(Color.Blue)
.fillMaxSize()
)
}