## Offset Translations: [简体中文](offset.zh.md) > [!TIP] > * The following example takes precedence over the Compose version component for demonstration > * [ZoomState].zoomable is equivalent to [ZoomImageView].zoomable > * [ZoomState].subsampling is equivalent to [ZoomImageView].subsampling ZoomImage supports one-finger drag, inertial swipe, keyboard drag, and the `offset()` method to move the image. ### One Finger Drag ZoomImage enables one finger drag gestures by default, but you can turn it off as follows: ```kotlin val zoomState: ZoomState by rememberSketchZoomState() zoomState.zoomable.setDisabledGestureTypes( zoomState.zoomable.disabledGestureTypes or GestureType.ONE_FINGER_DRAG ) SketchZoomAsyncImage( uri = "https://sample.com/sample.jpeg", contentDescription = "view image", modifier = Modifier.fillMaxSize(), zoomState = zoomState, ) ``` ### Keyboard drag ZoomImage supports drag images through the keyboard, supports both short press and long press operations. And the following keys are registered by default: * move up: Key.DirectionUp * move down: Key.DirectionDown * move left: Key.DirectionLeft * move right: Key.DirectionRight Since the keyboard drag function must rely on focus, and focus management is very complex, it is not enabled by default. You need to actively configure and request focus, as follows: ```kotlin val focusRequester = remember { FocusRequester() } val zoomState = rememberSketchZoomState() SketchZoomAsyncImage( uri = "https://sample.com/sample.jpeg", contentDescription = "view image", zoomState = zoomState, modifier = Modifier.fillMaxSize() .focusRequester(focusRequester) .focusable() .keyZoom(zoomState.zoomable), ) LaunchedEffect(Unit) { focusRequester.requestFocus() } ``` > [!TIP] > When requesting focus in HorizontalPager, you need to note that you can only request focus for the > current page, otherwise it will cause unexpected accidents. You can also turn it off dynamically via gesture control, as follows: ```kotlin val zoomState: ZoomState by rememberSketchZoomState() zoomState.zoomable.setDisabledGestureTypes( zoomState.zoomable.disabledGestureTypes or GestureType.KEYBOARD_DRAG ) SketchZoomAsyncImage( uri = "https://sample.com/sample.jpeg", contentDescription = "view image", modifier = Modifier.fillMaxSize(), zoomState = zoomState, ) ``` ### offset() ZoomImage provides a modified `offset()` method to move the image to a specified position, which has two parameters: * `targetOffset: Offset`: The target offset, with the offset origin being the upper-left corner of the component * `animated: Boolean = false`: Whether to use animation, the default is false Example: ```kotlin val zoomState: ZoomState by rememberSketchZoomState() SketchZoomAsyncImage( uri = "https://sample.com/sample.jpeg", contentDescription = "view image", modifier = Modifier.fillMaxSize(), zoomState = zoomState, ) val coroutineScope = rememberCoroutineScope() Button( onClick = { coroutineScope.launch { val targetOffset = zoomState.zoomable.transform.offset + Offset(x = 100, y = 200) zoomState.zoomable.offset(targetOffset = targetOffset, animated = true) } } ) { Text(text = "offset + Offset(100, 200)") } Button( onClick = { coroutineScope.launch { val targetOffset = zoomState.zoomable.transform.offset - Offset(x = 100, y = 200) zoomState.zoomable.offset(targetOffset = targetOffset, animated = true) } } ) { Text(text = "offset - Offset(100, 200)") } ``` ### Limit the bounds of offset By default, zoomImage can drag to view the entire content of the image regardless of what you set [ContentScale], for example, if you set [ContentScale] to Crop and [Alignment] to Center, then only the middle part of the image is displayed by default, and then you can also drag with one or two fingers to view the entire content of the image If you want the image to be moved only within the area restricted by [ContentScale] and [Alignment], and not the entire content, you can modify the `limitOffsetWithinBaseVisibleRect parameter` to true to achieve this Example: ```kotlin val zoomState: ZoomState by rememberSketchZoomState() zoomState.zoomable.setLimitOffsetWithinBaseVisibleRect(true) SketchZoomAsyncImage( uri = "https://sample.com/sample.jpeg", contentDescription = "view image", modifier = Modifier.fillMaxSize(), zoomState = zoomState, ) ``` ### Container Whitespace By default, ZoomImage always aligns the edge of the image with the edge of the container when dragging the image, and there will be no white space between them (except in the initial state of the image). When you need to leave a white space between the image and the container, you can pass To achieve this, set the `containerWhitespace` or `containerWhitespaceMultiple` parameter to Example: ```kotlin val zoomState: ZoomState by rememberSketchZoomState() // Set the specific size through the containerWhitespace property zoomState.zoomable.setContainerWhitespace( ContainerWhitespace(left = 4f, top = 3f, right = 2f, bottom = 1f) ) // or zoomState.zoomable.setContainerWhitespace(ContainerWhitespace(horizontal = 2f, vertical = 1f)) // or zoomState.zoomable.setContainerWhitespace(ContainerWhitespace(size = 1f)) // Leave 50% of the container size white space between the edge of the image and the edge of the container zoomState.zoomable.setContainerWhitespaceMultiple(0.5f) SketchZoomAsyncImage( uri = "https://sample.com/sample.jpeg", contentDescription = "view image", modifier = Modifier.fillMaxSize(), zoomState = zoomState, ) ``` ### Public Properties ```kotlin // compose val zoomState: ZoomState by rememberSketchZoomState() SketchZoomAsyncImage(zoomState = zoomState) val zoomable: ZoomableState = zoomState.zoomable // view val sketchZoomImageView = SketchZoomImageView(context) val zoomable: ZoomableEngine = sketchZoomImageView.zoomable ``` > [!TIP] > Note: The relevant properties of the view version are wrapped in StateFlow, so its name is > suffixed with State compared to the compose version Readable properties: * `zoomable.alignment: Alignment`: The alignment of content in container is Alignment.TopStart by default * `zoomable.limitOffsetWithinBaseVisibleRect: Boolean`: Whether to limit the offset to contentBaseVisibleRect, the default is false * `zoomable.containerWhitespaceMultiple: Float`: Add blank space around the container based on multiples of the container size, the default is 0f * `zoomable.containerWhitespace: ContainerWhitespace`: The configuration of blank areas around the container has higher priority than containerWhitespaceMultiple, and the default is ContainerWhitespace.Zero * `zoomable.disabledGestureTypes: Int`: Configure the disabled gesture type, the default is 0 (no gesture is disabled), and multiple gesture types can be combined using the bits or actions of GestureType * `zoomable.transform.offset: Offset`: Current offset (baseTransform.offset + userTransform.offset) * `zoomable.baseTransform.offset: Offset`: The current base offset, affected by the alignment parameter and the rotate method * `zoomable.userTransform.offset: Offset`: The current user offset, affected by offset(), locate(), and user gesture dragging * `zoomable.contentBaseDisplayRectF: Rect`: The content region in the container after the baseTransform transformation * `zoomable.contentBaseDisplayRect: IntRect`: The content region in the container after the baseTransform transformation * `zoomable.contentBaseVisibleRectF: Rect`: The content is visible region to the user after the baseTransform transformation * `zoomable.contentBaseVisibleRect: IntRect`: The content is visible region to the user after the baseTransform transformation * `zoomable.contentDisplayRectF: Rect`: The content region in the container after the final transform transformation * `zoomable.contentDisplayRect: IntRect`: The content region in the container after the final transform transformation * `zoomable.contentVisibleRectF: Rect`: The content is visible region to the user after the final transform transformation * `zoomable.contentVisibleRect: IntRect`: The content is visible region to the user after the final transform transformation * `zoomable.sourceVisibleRectF: Rect`: contentVisibleRect maps to the area on the original image * `zoomable.sourceVisibleRect: IntRect`: contentVisibleRect maps to the area on the original image * `zoomable.scrollEdge: ScrollEdge`: Edge state for the current offset Interactive methods: * `zoomable.setLimitOffsetWithinBaseVisibleRect(Boolean)`: Set whether to limit offsets to contentBaseVisibleRect * `zoomable.setContainerWhitespaceMultiple(Float)`: Set multiples based on container size to add blank areas around the container * `zoomable.setContainerWhitespace(ContainerWhitespace)`: Set the configuration of blank areas around the container, with priority higher than containerWhitespaceMultiple * `zoomable.setDisabledGestureTypes(Int)`: Set the disabled gesture type, you can use the bits or actions of GestureType to combine multiple gesture types * `zoomable.offset()`: Offset content to the specified location * `zoomable.offsetBy()`: Offset as incremental content specified offset * `zoomable.touchPointToContentPoint(): IntOffset`: Convert the touch point to a point on the content, the origin is the upper left corner of the content * `zoomable.touchPointToContentPointF(): Offset`: Convert the touch point to a point on the content, the origin is the upper left corner of the content * `zoomable.sourceToDraw(Offset): Offset`: Convert the points on the original image to the points at the time of drawing, the origin is the upper left corner of the container * `zoomable.sourceToDraw(Rect): Rect`: Convert the rectangle on the original image to the rectangle when drawing, the origin is the upper left corner of the container * `zoomable.canScroll(): Boolean`: Determine whether the current content can scroll in the specified direction #### Listen property changed * The relevant properties of the compose version are wrapped in State and can be read directly in the Composable function to implement listening * The relevant properties of the view are wrapped in StateFlow, and its collect function can be called to implement the listening [ZoomImageView]: ../zoomimage-view/src/main/kotlin/com/github/panpf/zoomimage/ZoomImageView.kt [ZoomImage]: ../zoomimage-compose/src/commonMain/kotlin/com/github/panpf/zoomimage/ZoomImage.kt [ZoomState]: ../zoomimage-compose/src/commonMain/kotlin/com/github/panpf/zoomimage/compose/ZoomState.kt [ContentScale]: https://developer.android.com/reference/kotlin/androidx/compose/ui/layout/ContentScale [Alignment]: https://developer.android.com/reference/kotlin/androidx/compose/ui/Alignment [ZoomableState]: ../zoomimage-compose/src/commonMain/kotlin/com/github/panpf/zoomimage/compose/zoom/ZoomableState.kt [ScrollEdge]: ../zoomimage-core/src/commonMain/kotlin/com/github/panpf/zoomimage/zoom/ScrollEdge.kt