# TransformableState

Artifact: [androidx.compose.foundation:foundation](https://developer.android.com/jetpack/androidx/releases/compose-foundation) [View Source](https://cs.android.com/search?q=file:androidx/compose/foundation/gestures/TransformableState.kt+class:androidx.compose.foundation.gestures.TransformableState) Added in [1.0.0](https://developer.android.com/jetpack/androidx/releases/compose-foundation#1.0.0) Common/AllAndroid/JVM
- Cmn

  ```
  interface TransformableState
  ```

*** ** * ** ***

State of `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/transformable.modifier#(androidx.compose.ui.Modifier).transformable(androidx.compose.foundation.gestures.TransformableState,kotlin.Boolean,kotlin.Boolean)`. Allows for a granular control of how different gesture transformations are consumed by the user as well as to write custom transformation methods using `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState#transform(androidx.compose.foundation.MutatePriority,kotlin.coroutines.SuspendFunction1)` suspend function.

## Summary

| ### Public functions |
|---|---|---|
| `suspend https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState#transform(androidx.compose.foundation.MutatePriority,kotlin.coroutines.SuspendFunction1)(transformPriority: https://developer.android.com/reference/kotlin/androidx/compose/foundation/MutatePriority, block: suspend https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformScope.() -> https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html)` Call this function to take control of transformations and gain the ability to send transform events via `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformScope#transformBy(kotlin.Float,androidx.compose.ui.geometry.Offset,kotlin.Float)`. | Cmn |

| ### Public properties |
|---|---|---|
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-boolean/index.html` | `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState#isTransformInProgress()` Whether this `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState` is currently transforming by gesture or programmatically or not. | Cmn |

| ### Extension functions |
|---|---|---|
| `suspend https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState.https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState#(androidx.compose.foundation.gestures.TransformableState).animateBy(kotlin.Float,androidx.compose.ui.geometry.Offset,kotlin.Float,androidx.compose.animation.core.AnimationSpec,androidx.compose.animation.core.AnimationSpec,androidx.compose.animation.core.AnimationSpec,androidx.compose.ui.geometry.Offset)( zoomFactor: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html, panOffset: https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset, rotationDegrees: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html, zoomAnimationSpec: https://developer.android.com/reference/kotlin/androidx/compose/animation/core/AnimationSpec<https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html>, panAnimationSpec: https://developer.android.com/reference/kotlin/androidx/compose/animation/core/AnimationSpec<https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset>, rotationAnimationSpec: https://developer.android.com/reference/kotlin/androidx/compose/animation/core/AnimationSpec<https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html>, centroid: https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset )` Animate zoom, pan, and rotation simultaneously and suspend until the animation is finished. | Cmn |
| `suspend https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState.https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState#(androidx.compose.foundation.gestures.TransformableState).animatePanBy(androidx.compose.ui.geometry.Offset,androidx.compose.animation.core.AnimationSpec,androidx.compose.ui.geometry.Offset)( offset: https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset, animationSpec: https://developer.android.com/reference/kotlin/androidx/compose/animation/core/AnimationSpec<https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset>, centroid: https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset )` Animate pan by `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).animatePanBy(androidx.compose.ui.geometry.Offset,androidx.compose.animation.core.AnimationSpec,androidx.compose.ui.geometry.Offset)` Offset in pixels and suspend until its finished | Cmn |
| `suspend https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState.https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState#(androidx.compose.foundation.gestures.TransformableState).animateRotateBy(kotlin.Float,androidx.compose.animation.core.AnimationSpec,androidx.compose.ui.geometry.Offset)( degrees: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html, animationSpec: https://developer.android.com/reference/kotlin/androidx/compose/animation/core/AnimationSpec<https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html>, centroid: https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset )` Animate rotate by a ratio of `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).animateRotateBy(kotlin.Float,androidx.compose.animation.core.AnimationSpec,androidx.compose.ui.geometry.Offset)` clockwise and suspend until its finished. | Cmn |
| `suspend https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState.https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState#(androidx.compose.foundation.gestures.TransformableState).animateZoomBy(kotlin.Float,androidx.compose.animation.core.AnimationSpec,androidx.compose.ui.geometry.Offset)( zoomFactor: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html, animationSpec: https://developer.android.com/reference/kotlin/androidx/compose/animation/core/AnimationSpec<https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html>, centroid: https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset )` Animate zoom by a ratio of `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).animateZoomBy(kotlin.Float,androidx.compose.animation.core.AnimationSpec,androidx.compose.ui.geometry.Offset)` over the current size and suspend until its finished. | Cmn |
| `suspend https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState.https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState#(androidx.compose.foundation.gestures.TransformableState).panBy(androidx.compose.ui.geometry.Offset,androidx.compose.ui.geometry.Offset)(offset: https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset, centroid: https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset)` Pan without animation by a `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).panBy(androidx.compose.ui.geometry.Offset,androidx.compose.ui.geometry.Offset)` Offset in pixels and suspend until it's set. | Cmn |
| `suspend https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState.https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState#(androidx.compose.foundation.gestures.TransformableState).rotateBy(kotlin.Float,androidx.compose.ui.geometry.Offset)(degrees: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html, centroid: https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset)` Rotate without animation by a `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).rotateBy(kotlin.Float,androidx.compose.ui.geometry.Offset)` degrees and suspend until it's set. | Cmn |
| `suspend https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState.https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState#(androidx.compose.foundation.gestures.TransformableState).stopTransformation(androidx.compose.foundation.MutatePriority)( terminationPriority: https://developer.android.com/reference/kotlin/androidx/compose/foundation/MutatePriority )` Stop and suspend until any ongoing `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState#transform(androidx.compose.foundation.MutatePriority,kotlin.coroutines.SuspendFunction1)` with priority `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).stopTransformation(androidx.compose.foundation.MutatePriority)` or lower is terminated. | Cmn |
| `suspend https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState.https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState#(androidx.compose.foundation.gestures.TransformableState).zoomBy(kotlin.Float,androidx.compose.ui.geometry.Offset)(zoomFactor: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html, centroid: https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset)` Zoom without animation by a ratio of `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).zoomBy(kotlin.Float,androidx.compose.ui.geometry.Offset)` over the current size and suspend until it's set. | Cmn |

## Public functions

### transform

Cmn Added in [1.0.0](https://developer.android.com/jetpack/androidx/releases/compose-foundation#1.0.0)

```
suspend fun transform(
    transformPriority: MutatePriority = MutatePriority.Default,
    block: suspend TransformScope.() -> Unit
): Unit
```

Call this function to take control of transformations and gain the ability to send transform events via `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformScope#transformBy(kotlin.Float,androidx.compose.ui.geometry.Offset,kotlin.Float)`. All actions that change zoom, pan or rotation values must be performed within a `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState#transform(androidx.compose.foundation.MutatePriority,kotlin.coroutines.SuspendFunction1)` block (even if they don't call any other methods on this object) in order to guarantee that mutual exclusion is enforced.

If `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState#transform(androidx.compose.foundation.MutatePriority,kotlin.coroutines.SuspendFunction1)` is called from elsewhere with the `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState#transform(androidx.compose.foundation.MutatePriority,kotlin.coroutines.SuspendFunction1)` higher or equal to ongoing transform, ongoing transform will be canceled.

## Public properties

### isTransformInProgress

Cmn Added in [1.0.0](https://developer.android.com/jetpack/androidx/releases/compose-foundation#1.0.0)

```
val isTransformInProgress: Boolean
```

Whether this `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState` is currently transforming by gesture or programmatically or not.

## Extension functions

### TransformableState.animateBy

Cmn Artifact: [androidx.compose.foundation:foundation](https://developer.android.com/jetpack/androidx/releases/compose-foundation) [View Source](https://cs.android.com/search?q=file:androidx/compose/foundation/gestures/TransformableState.kt+function:animateBy)

```
suspend fun TransformableState.animateBy(
    zoomFactor: Float,
    panOffset: Offset,
    rotationDegrees: Float,
    zoomAnimationSpec: AnimationSpec<Float> = SpringSpec(stiffness = Spring.StiffnessLow),
    panAnimationSpec: AnimationSpec<Offset> = SpringSpec(stiffness = Spring.StiffnessLow),
    rotationAnimationSpec: AnimationSpec<Float> = SpringSpec(stiffness = Spring.StiffnessLow),
    centroid: Offset = Offset.Unspecified
): Unit
```

Animate zoom, pan, and rotation simultaneously and suspend until the animation is finished.

Zoom is animated by a ratio of `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).animateBy(kotlin.Float,androidx.compose.ui.geometry.Offset,kotlin.Float,androidx.compose.animation.core.AnimationSpec,androidx.compose.animation.core.AnimationSpec,androidx.compose.animation.core.AnimationSpec,androidx.compose.ui.geometry.Offset)` over the current size. Pan is animated by `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).animateBy(kotlin.Float,androidx.compose.ui.geometry.Offset,kotlin.Float,androidx.compose.animation.core.AnimationSpec,androidx.compose.animation.core.AnimationSpec,androidx.compose.animation.core.AnimationSpec,androidx.compose.ui.geometry.Offset)` in pixels. Rotation is animated by the value of `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).animateBy(kotlin.Float,androidx.compose.ui.geometry.Offset,kotlin.Float,androidx.compose.animation.core.AnimationSpec,androidx.compose.animation.core.AnimationSpec,androidx.compose.animation.core.AnimationSpec,androidx.compose.ui.geometry.Offset)` clockwise. Any of these parameters can be set to a no-op value that will result in no animation of that parameter. The no-op values are the following: `1f` for `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).animateBy(kotlin.Float,androidx.compose.ui.geometry.Offset,kotlin.Float,androidx.compose.animation.core.AnimationSpec,androidx.compose.animation.core.AnimationSpec,androidx.compose.animation.core.AnimationSpec,androidx.compose.ui.geometry.Offset)`, `Offset.Zero` for `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).animateBy(kotlin.Float,androidx.compose.ui.geometry.Offset,kotlin.Float,androidx.compose.animation.core.AnimationSpec,androidx.compose.animation.core.AnimationSpec,androidx.compose.animation.core.AnimationSpec,androidx.compose.ui.geometry.Offset)`, and `0f` for `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).animateBy(kotlin.Float,androidx.compose.ui.geometry.Offset,kotlin.Float,androidx.compose.animation.core.AnimationSpec,androidx.compose.animation.core.AnimationSpec,androidx.compose.animation.core.AnimationSpec,androidx.compose.ui.geometry.Offset)`.

```kotlin
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.gestures.animateBy
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.gestures.rememberTransformableState
import androidx.compose.foundation.gestures.transformable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material3.Text
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.geometry.center
import androidx.compose.ui.geometry.isSpecified
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.unit.toSize

/**
 * 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())
}

Box(Modifier.size(200.dp).clipToBounds().background(Color.LightGray)) {
    // set up all transformation states
    var scale by remember { mutableStateOf(1f) }
    var rotation by remember { mutableStateOf(0f) }
    var offset by remember { mutableStateOf(Offset.Zero) }
    val coroutineScope = rememberCoroutineScope()

    var size by remember { mutableStateOf(Size.Zero) }

    // let's create a modifier state to specify how to update our UI state defined above
    val state =
        rememberTransformableState { centroid, zoomChange, offsetChange, rotationChange ->
            val oldScale = scale
            val newScale = max(scale * zoomChange, 1f)

            // If the centroid isn't specified, assume it should be applied from the center
            val effectiveCentroid = centroid.takeIf { it.isSpecified } ?: size.center

            // 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 + effectiveCentroid / oldScale).rotateBy(rotationChange) -
                    (effectiveCentroid / newScale + offsetChange / oldScale)
            scale = newScale
            rotation += rotationChange
        }
    Box(
        Modifier.onSizeChanged { size = it.toSize() }
            // add transformable to listen to multitouch transformation events after offset
            .transformable(state = state)
            // detect tap gestures:
            // 1) single tap to simultaneously animate zoom, pan, and rotation
            // 2) double tap to animate back to the initial position
            .pointerInput(Unit) {
                detectTapGestures(
                    onTap = { offset ->
                        coroutineScope.launch {
                            state.animateBy(
                                zoomFactor = 1.5f,
                                panOffset = Offset(20f, 20f),
                                rotationDegrees = 90f,
                                zoomAnimationSpec = spring(),
                                panAnimationSpec = tween(durationMillis = 1000),
                                rotationAnimationSpec = spring(),
                                centroid = offset,
                            )
                        }
                    },
                    onDoubleTap = { offset ->
                        coroutineScope.launch {
                            state.animateBy(
                                zoomFactor = 1 / scale,
                                panOffset = -offset,
                                rotationDegrees = -rotation,
                                centroid = offset,
                            )
                        }
                    },
                )
            }
            .fillMaxSize()
            .border(1.dp, Color.Green)
    ) {
        Text(
            "\uD83C\uDF55",
            fontSize = 32.sp,
            modifier =
                Modifier.fillMaxSize()
                    .graphicsLayer {
                        translationX = -offset.x * scale
                        translationY = -offset.y * scale
                        scaleX = scale
                        scaleY = scale
                        rotationZ = rotation
                        transformOrigin = TransformOrigin(0f, 0f)
                    }
                    .wrapContentSize(align = Alignment.Center),
        )
    }
}
```

| Parameters |
|---|---|
| `zoomFactor: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html` | ratio over the current size by which to zoom. For example, if `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).animateBy(kotlin.Float,androidx.compose.ui.geometry.Offset,kotlin.Float,androidx.compose.animation.core.AnimationSpec,androidx.compose.animation.core.AnimationSpec,androidx.compose.animation.core.AnimationSpec,androidx.compose.ui.geometry.Offset)` is `3f`, zoom will be increased 3 fold from the current value. |
| `panOffset: https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset` | offset to pan, in pixels |
| `rotationDegrees: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html` | the degrees by which to rotate clockwise |
| `zoomAnimationSpec: https://developer.android.com/reference/kotlin/androidx/compose/animation/core/AnimationSpec<https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html> = SpringSpec(stiffness = Spring.StiffnessLow)` | `https://developer.android.com/reference/kotlin/androidx/compose/animation/core/AnimationSpec` to be used for animating zoom |
| `panAnimationSpec: https://developer.android.com/reference/kotlin/androidx/compose/animation/core/AnimationSpec<https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset> = SpringSpec(stiffness = Spring.StiffnessLow)` | `https://developer.android.com/reference/kotlin/androidx/compose/animation/core/AnimationSpec` to be used for animating offset |
| `rotationAnimationSpec: https://developer.android.com/reference/kotlin/androidx/compose/animation/core/AnimationSpec<https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html> = SpringSpec(stiffness = Spring.StiffnessLow)` | `https://developer.android.com/reference/kotlin/androidx/compose/animation/core/AnimationSpec` to be used for animating rotation |
| `centroid: https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset = Offset.Unspecified` | the `https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset` around which the animation should occur, if any. The default value is `https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset#Unspecified()`, which leaves the behavior up to the implementation of the `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState`. |

### TransformableState.animatePanBy

Cmn Artifact: [androidx.compose.foundation:foundation](https://developer.android.com/jetpack/androidx/releases/compose-foundation) [View Source](https://cs.android.com/search?q=file:androidx/compose/foundation/gestures/TransformableState.kt+function:animatePanBy)

```
suspend fun TransformableState.animatePanBy(
    offset: Offset,
    animationSpec: AnimationSpec<Offset> = SpringSpec(stiffness = Spring.StiffnessLow),
    centroid: Offset = Offset.Unspecified
): Unit
```

Animate pan by `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).animatePanBy(androidx.compose.ui.geometry.Offset,androidx.compose.animation.core.AnimationSpec,androidx.compose.ui.geometry.Offset)` Offset in pixels and suspend until its finished

| Parameters |
|---|---|
| `offset: https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset` | offset to pan, in pixels |
| `animationSpec: https://developer.android.com/reference/kotlin/androidx/compose/animation/core/AnimationSpec<https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset> = SpringSpec(stiffness = Spring.StiffnessLow)` | `https://developer.android.com/reference/kotlin/androidx/compose/animation/core/AnimationSpec` to be used for pan animation |
| `centroid: https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset = Offset.Unspecified` | the `https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset` around which the pan should occur, if any. The default value is `https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset#Unspecified()`, which leaves the behavior up to the implementation of the `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState`. |

### TransformableState.animateRotateBy

Cmn Artifact: [androidx.compose.foundation:foundation](https://developer.android.com/jetpack/androidx/releases/compose-foundation) [View Source](https://cs.android.com/search?q=file:androidx/compose/foundation/gestures/TransformableState.kt+function:animateRotateBy)

```
suspend fun TransformableState.animateRotateBy(
    degrees: Float,
    animationSpec: AnimationSpec<Float> = SpringSpec(stiffness = Spring.StiffnessLow),
    centroid: Offset = Offset.Unspecified
): Unit
```

Animate rotate by a ratio of `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).animateRotateBy(kotlin.Float,androidx.compose.animation.core.AnimationSpec,androidx.compose.ui.geometry.Offset)` clockwise and suspend until its finished.

| Parameters |
|---|---|
| `degrees: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html` | the degrees by which to rotate clockwise |
| `animationSpec: https://developer.android.com/reference/kotlin/androidx/compose/animation/core/AnimationSpec<https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html> = SpringSpec(stiffness = Spring.StiffnessLow)` | `https://developer.android.com/reference/kotlin/androidx/compose/animation/core/AnimationSpec` to be used for animation |
| `centroid: https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset = Offset.Unspecified` | the `https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset` around which the rotation should occur, if any. The default value is `https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset#Unspecified()`, which leaves the behavior up to the implementation of the `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState`. |

### TransformableState.animateZoomBy

Cmn Artifact: [androidx.compose.foundation:foundation](https://developer.android.com/jetpack/androidx/releases/compose-foundation) [View Source](https://cs.android.com/search?q=file:androidx/compose/foundation/gestures/TransformableState.kt+function:animateZoomBy)

```
suspend fun TransformableState.animateZoomBy(
    zoomFactor: Float,
    animationSpec: AnimationSpec<Float> = SpringSpec(stiffness = Spring.StiffnessLow),
    centroid: Offset = Offset.Unspecified
): Unit
```

Animate zoom by a ratio of `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).animateZoomBy(kotlin.Float,androidx.compose.animation.core.AnimationSpec,androidx.compose.ui.geometry.Offset)` over the current size and suspend until its finished.

| Parameters |
|---|---|
| `zoomFactor: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html` | ratio over the current size by which to zoom. For example, if `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).animateZoomBy(kotlin.Float,androidx.compose.animation.core.AnimationSpec,androidx.compose.ui.geometry.Offset)` is `3f`, zoom will be increased 3 fold from the current value. |
| `animationSpec: https://developer.android.com/reference/kotlin/androidx/compose/animation/core/AnimationSpec<https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html> = SpringSpec(stiffness = Spring.StiffnessLow)` | `https://developer.android.com/reference/kotlin/androidx/compose/animation/core/AnimationSpec` to be used for animation |
| `centroid: https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset = Offset.Unspecified` | the `https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset` around which the zoom should occur, if any. The default value is `https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset#Unspecified()`, which leaves the behavior up to the implementation of the `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState`. |

### TransformableState.panBy

Cmn Artifact: [androidx.compose.foundation:foundation](https://developer.android.com/jetpack/androidx/releases/compose-foundation) [View Source](https://cs.android.com/search?q=file:androidx/compose/foundation/gestures/TransformableState.kt+function:panBy)

```
suspend fun TransformableState.panBy(
    offset: Offset,
    centroid: Offset = Offset.Unspecified
): Unit
```

Pan without animation by a `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).panBy(androidx.compose.ui.geometry.Offset,androidx.compose.ui.geometry.Offset)` Offset in pixels and suspend until it's set.

| Parameters |
|---|---|
| `offset: https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset` | offset in pixels by which to pan |
| `centroid: https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset = Offset.Unspecified` | the `https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset` around which the pan should occur, if any. The default value is `https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset#Unspecified()`, which leaves the behavior up to the implementation of the `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState`. |

### TransformableState.rotateBy

Cmn Artifact: [androidx.compose.foundation:foundation](https://developer.android.com/jetpack/androidx/releases/compose-foundation) [View Source](https://cs.android.com/search?q=file:androidx/compose/foundation/gestures/TransformableState.kt+function:rotateBy)

```
suspend fun TransformableState.rotateBy(
    degrees: Float,
    centroid: Offset = Offset.Unspecified
): Unit
```

Rotate without animation by a `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).rotateBy(kotlin.Float,androidx.compose.ui.geometry.Offset)` degrees and suspend until it's set.

| Parameters |
|---|---|
| `degrees: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html` | degrees by which to rotate |
| `centroid: https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset = Offset.Unspecified` | the `https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset` around which the rotation should occur, if any. The default value is `https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset#Unspecified()`, which leaves the behavior up to the implementation of the `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState`. |

### TransformableState.stopTransformation

Cmn Artifact: [androidx.compose.foundation:foundation](https://developer.android.com/jetpack/androidx/releases/compose-foundation) [View Source](https://cs.android.com/search?q=file:androidx/compose/foundation/gestures/TransformableState.kt+function:stopTransformation)

```
suspend fun TransformableState.stopTransformation(
    terminationPriority: MutatePriority = MutatePriority.Default
): Unit
```

Stop and suspend until any ongoing `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState#transform(androidx.compose.foundation.MutatePriority,kotlin.coroutines.SuspendFunction1)` with priority `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).stopTransformation(androidx.compose.foundation.MutatePriority)` or lower is terminated.

| Parameters |
|---|---|
| `terminationPriority: https://developer.android.com/reference/kotlin/androidx/compose/foundation/MutatePriority = MutatePriority.Default` | transformation that runs with this priority or lower will be stopped |

### TransformableState.zoomBy

Cmn Artifact: [androidx.compose.foundation:foundation](https://developer.android.com/jetpack/androidx/releases/compose-foundation) [View Source](https://cs.android.com/search?q=file:androidx/compose/foundation/gestures/TransformableState.kt+function:zoomBy)

```
suspend fun TransformableState.zoomBy(
    zoomFactor: Float,
    centroid: Offset = Offset.Unspecified
): Unit
```

Zoom without animation by a ratio of `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.foundation.gestures.TransformableState).zoomBy(kotlin.Float,androidx.compose.ui.geometry.Offset)` over the current size and suspend until it's set.

| Parameters |
|---|---|
| `zoomFactor: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html` | ratio over the current size by which to zoom |
| `centroid: https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset = Offset.Unspecified` | the `https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset` around which the zoom should occur, if any. The default value is `https://developer.android.com/reference/kotlin/androidx/compose/ui/geometry/Offset#Unspecified()`, which leaves the behavior up to the implementation of the `https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/TransformableState`. |