{"id":19521805,"url":"https://github.com/smarttoolfactory/compose-zoom","last_synced_at":"2025-04-26T09:31:59.653Z","repository":{"id":57765215,"uuid":"527633778","full_name":"SmartToolFactory/Compose-Zoom","owner":"SmartToolFactory","description":"🚀🏞🔍 Zoom Modifiers, zoomable image and layouts with limit pan bounds, fling and moving back to valid bounds and callbacks that return current transformation or visible image section","archived":false,"fork":false,"pushed_at":"2022-10-03T18:50:23.000Z","size":1846,"stargazers_count":56,"open_issues_count":3,"forks_count":7,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-04T10:26:31.630Z","etag":null,"topics":["android","android-zoom","jetpack-compose","zoom-images"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SmartToolFactory.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-08-22T15:56:02.000Z","updated_at":"2025-03-08T03:12:55.000Z","dependencies_parsed_at":"2022-08-22T19:11:00.665Z","dependency_job_id":null,"html_url":"https://github.com/SmartToolFactory/Compose-Zoom","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SmartToolFactory%2FCompose-Zoom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SmartToolFactory%2FCompose-Zoom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SmartToolFactory%2FCompose-Zoom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SmartToolFactory%2FCompose-Zoom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SmartToolFactory","download_url":"https://codeload.github.com/SmartToolFactory/Compose-Zoom/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250967130,"owners_count":21515542,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","android-zoom","jetpack-compose","zoom-images"],"created_at":"2024-11-11T00:35:00.616Z","updated_at":"2025-04-26T09:31:58.732Z","avatar_url":"https://github.com/SmartToolFactory.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Compose Zoom\n\n[![](https://jitpack.io/v/SmartToolFactory/Compose-Zoom.svg)](https://jitpack.io/#SmartToolFactory/Compose-Zoom)\n\nZoom Modifiers, zoomable image and layouts with limit pan bounds, fling and moving back to\nvalid bounds and callbacks that return current transformation or visible image section\n\n\nhttps://user-images.githubusercontent.com/35650605/186409295-14e967b5-a31c-4b2c-ab38-1990cbdce13a.mp4\n\n\n## Gradle Setup\n\nTo get a Git project into your build:\n\n* Step 1. Add the JitPack repository to your build file Add it in your root build.gradle at the end\n  of repositories:\n\n```\nallprojects {\n  repositories {\n      ...\n      maven { url 'https://jitpack.io' }\n  }\n}\n```\n\n* Step 2. Add the dependency\n\n```\ndependencies {\n    implementation 'com.github.SmartToolFactory:Compose-Zoom:\u003cversion\u003e'\n}\n```\n\n## Modifier.zoom()\n\nModifier that zooms, pans, and rotates any Composable it set to. when [clip] is true\n`Modifier.clipToBounds()` is used to limit content inside Composable bounds\n`consume` param is for `Modifier.pointerInput` to consume current events to prevent other\ngestures like scroll, drag or transform to initiate.\nCallbacks notify user that gesture has started, going on finished with [ZoomData] that\ncontains current transformation information\n\n```kotlin\nfun Modifier.zoom(\n    key: Any? = Unit,\n    consume: Boolean = true,\n    clip: Boolean = true,\n    zoomState: ZoomState,\n    onGestureStart: ((ZoomData) -\u003e Unit)? = null,\n    onGesture: ((ZoomData) -\u003e Unit)? = null,\n    onGestureEnd: ((ZoomData) -\u003e Unit)? = null\n)\n```\n\n### Parameters\n\n* **key/key1-key2/keys** are used for restarting `Modifier.pointerInput(*keys)` and remember\n  for getting `ZoomState`\n* **consume** flag to prevent other gestures such as scroll, drag or transform to get\n* **clip** when set to true clips to parent bounds. Anything outside parent bounds is not drawn\n  empty space on sides or edges of parent.\n* **zoomState** State of the zoom that contains option to set initial, min, max zoom, enabling\n  rotation, pan or zoom and contains current [ZoomData]event propagations\n* **onGestureStart** callback to to notify gesture has started and return current ZoomData of this\n  modifier\n* **onGesture** callback to notify about ongoing gesture and return current ZoomData of this\n  modifier\n* **onGestureEnd** callback to notify that gesture finished and return current ZoomData of this\n  modifier\n\n## ZoomState\n\nCreate and [remember] the [ZoomState] based on the currently appropriate transform configuration to\nallow changing pan, zoom, and rotation.\n\n```kotlin\n@Composable\nfun rememberZoomState(\n    initialZoom: Float = 1f,\n    initialRotation: Float = 0f,\n    minZoom: Float = 1f,\n    maxZoom: Float = 5f,\n    zoomable: Boolean = true,\n    pannable: Boolean = true,\n    rotatable: Boolean = false,\n    limitPan: Boolean = false,\n    key1: Any? = Unit\n): ZoomState {\n    return remember(key1) {\n        ZoomState(\n            initialZoom = initialZoom,\n            initialRotation = initialRotation,\n            minZoom = minZoom,\n            maxZoom = maxZoom,\n            zoomable = zoomable,\n            pannable = pannable,\n            rotatable = rotatable,\n            limitPan = limitPan\n        )\n    }\n}\n```\n\n### Parameters\n\n* **initialZoom** zoom set initially\n* **initialRotation** rotation set initially\n* **minZoom** minimum zoom value\n* **maxZoom** maximum zoom value\n* **limitPan** limits pan to bounds of parent Composable. Using this flag prevents creating empty\n  space on sides or edges of parent\n* **zoomable** when set to true zoom is enabled\n* **pannable** when set to true pan is enabled\n* **rotatable** when set to true rotation is enabled\n\n## ZoomableImage\n\nZoomable image that zooms in and out in [ [minZoom], [maxZoom] ] interval and translates\nzoomed image based on pointer position.\nDouble tap gestures reset image translation and zoom to default values with animation.\nCallbacks notify user that gesture has started, going on finished with [ZoomData] that\ncontains current transformation information\n\n```kotlin\n@Composable\nfun ZoomableImage(\n    modifier: Modifier = Modifier,\n    imageBitmap: ImageBitmap,\n    alignment: Alignment = Alignment.Center,\n    contentScale: ContentScale = ContentScale.Fit,\n    contentDescription: String? = null,\n    alpha: Float = DefaultAlpha,\n    colorFilter: ColorFilter? = null,\n    filterQuality: FilterQuality = DrawScope.DefaultFilterQuality,\n    initialZoom: Float = 1f,\n    minZoom: Float = 1f,\n    maxZoom: Float = 5f,\n    limitPan: Boolean = true,\n    zoomable: Boolean = true,\n    pannable: Boolean = true,\n    rotatable: Boolean = false,\n    clip: Boolean = true,\n    clipTransformToContentScale: Boolean = false,\n    consume: Boolean = true,\n    onGestureStart: (ZoomData) -\u003e Unit = {},\n    onGesture: (ZoomData) -\u003e Unit = {},\n    onGestureEnd: (ZoomData) -\u003e Unit = {}\n)\n```\n\n### Parameters\n\n* **initialZoom** zoom set initially\n* **minZoom** minimum zoom value this Composable can possess\n* **maxZoom** maximum zoom value this Composable can possess\n* **clip** whether image should be clip to bounds of Image\n* **clipTransformToContentScale** when set true zoomable image takes borders of image drawn while\n  zooming in. [contentScale] determines whether will be empty spaces on edges of Composable\n* **limitPan** limits pan to bounds of parent Composable. Using this flag prevents creating empty\n  space on sides or edges of parent.\n* **consume** flag to prevent other gestures such as scroll, drag or transform to get event\n  propagations\n* **zoomable** when set to true zoom is enabled\n* **pannable** when set to true pan is enabled\n* **rotatable** when set to true rotation is enabled\n* **onGestureStart** callback to to notify gesture has started and return current ZoomData of this\n  modifier\n* **onGesture** callback to notify about ongoing gesture and return current ZoomData of this\n  modifier\n* **onGestureEnd** callback to notify that gesture finished and return current ZoomData of this\n  modifier\n\n## Modifier.enhancedZoom()\n\nModifier that zooms in or out of Composable set to. This zoom modifier has option to move back to\nbounds with an animation or option to have fling gesture when user removes from screen while\nvelocity is higher than threshold to have smooth touch effect.\n\n```kotlin\nfun Modifier.enhancedZoom(\n    key: Any? = Unit,\n    clip: Boolean = true,\n    enhancedZoomState: EnhancedZoomState,\n    enabled: (Float, Offset, Float) -\u003e Boolean = DefaultEnabled,\n    zoomOnDoubleTap: (ZoomLevel) -\u003e Float = enhancedZoomState.DefaultOnDoubleTap,\n    onGestureStart: ((EnhancedZoomData) -\u003e Unit)? = null,\n    onGesture: ((EnhancedZoomData) -\u003e Unit)? = null,\n    onGestureEnd: ((EnhancedZoomData) -\u003e Unit)? = null,\n)\n```\n\n### Parameters\n\n* **key** is used for [Modifier.pointerInput] to restart closure when any keys assigned change\n* **clip** when set to true clips to parent bounds. Anything outside parent bounds is not drawn\n  empty space on sides or edges of parent.\n* **enhancedZoomState** State of the zoom that contains option to set initial, min, max zoom,\n  enabling rotation, pan or zoom and contains current [EnhancedZoomData]event propagations. Also\n  contains [Rect] of visible area based on pan, zoom and rotation\n* **zoomOnDoubleTap** lambda that returns current [ZoomLevel] and based on current level enables\n  developer to define zoom on double tap gesture\n* **enabled** lambda can be used selectively enable or disable pan and intercepting with scroll,\n  drag or lists or pagers using current zoom, pan or rotation values\n* **onGestureStart** callback to to notify gesture has started and return current [EnhancedZoomData]\n  of this modifier\n* **onGesture** callback to notify about ongoing gesture and return current [EnhancedZoomData]  of\n  this modifier\n* **onGestureEnd** callback to notify that gesture finished return current [EnhancedZoomData]  of\n  this modifier\n\n## EnhancedZoomState\n\nCreate and [remember] the [EnhancedZoomState] based on the currently appropriate transform\nconfiguration to allow changing pan, zoom, and rotation.\nAllows to change zoom, pan, translate, or get current state by calling methods on this object. To be\nhosted and passed to [Modifier.enhancedZoom].\nAlso contains [EnhancedZoomData] about current transformation area of Composable and visible are of\nimage being zoomed, rotated, or panned. If any animation is going on\ncurrent [EnhancedZoomState.isAnimationRunning] is true and [EnhancedZoomData] returns rectangle that\nbelongs to end of animation.\n\n```kotlin\nfun Modifier.enhancedZoom(\n    key: Any? = Unit,\n    clip: Boolean = true,\n    enhancedZoomState: EnhancedZoomState,\n    enabled: (Float, Offset, Float) -\u003e Boolean = DefaultEnabled,\n    zoomOnDoubleTap: (ZoomLevel) -\u003e Float = enhancedZoomState.DefaultOnDoubleTap,\n    onGestureStart: ((EnhancedZoomData) -\u003e Unit)? = null,\n    onGesture: ((EnhancedZoomData) -\u003e Unit)? = null,\n    onGestureEnd: ((EnhancedZoomData) -\u003e Unit)? = null,\n)\n```\n\n### Parameters\n\n* **initialZoom** zoom set initially\n* **minZoom** minimum zoom value\n* **maxZoom** maximum zoom value\n* **fling** when set to true dragging pointer builds up velocity. When last pointer leaves\n  Composable a movement invoked against friction till velocity drops below to threshold\n* **moveToBounds** when set to true if image zoom is lower than initial zoom or panned out of image\n  boundaries moves back to bounds with animation.\n* **zoomable** when set to true zoom is enabled\n* **pannable** when set to true pan is enabled\n* **rotatable** when set to true rotation is enabled\n* **limitPan** limits pan to bounds of parent Composable. Using this flag prevents creating empty\n  space on sides or edges of parent\n\n## EnhancedZoomableImage\n\nZoomable image that zooms in and out in [ [minZoom], [maxZoom] ] interval and pans zoomed image\nbased on pointer position. Double tap gestures reset image translation and zoom to default values\nwith animation. Difference between `ZoomaableImage` and `EnhancedZoomableImage` is this version\ncan animate back to bounds and have fling gesture that doesn't stop movement when last pointer\nis up but continues motion agains friction.\n\n`moveToBound` is true image moves to bounds when moved out of bounds. When\n`fling` is set to true image moves until velocity drops below threshold.\n\n```\n@Composable\nfun EnhancedZoomableImage(\n    modifier: Modifier = Modifier,\n    imageBitmap: ImageBitmap,\n    alignment: Alignment = Alignment.Center,\n    contentScale: ContentScale = ContentScale.Fit,\n    contentDescription: String? = null,\n    alpha: Float = DefaultAlpha,\n    colorFilter: ColorFilter? = null,\n    filterQuality: FilterQuality = DrawScope.DefaultFilterQuality,\n    initialZoom: Float = 1f,\n    minZoom: Float = .5f,\n    maxZoom: Float = 5f,\n    limitPan: Boolean = true,\n    fling: Boolean = false,\n    moveToBounds: Boolean = true,\n    zoomable: Boolean = true,\n    pannable: Boolean = true,\n    rotatable: Boolean = false,\n    clip: Boolean = true,\n    enabled: (Float, Offset, Float) -\u003e Boolean = DefaultEnabled,\n    zoomOnDoubleTap: (ZoomLevel) -\u003e Float = DefaultOnDoubleTap,\n    clipTransformToContentScale: Boolean = false,\n    onGestureStart: ((EnhancedZoomData) -\u003e Unit)? = null,\n    onGesture: ((EnhancedZoomData) -\u003e Unit)? = null,\n    onGestureEnd: ((EnhancedZoomData) -\u003e Unit)? = null\n)\n```\n\n### Parameters\n\n* **initialZoom** zoom set initially\n* **minZoom** minimum zoom value this Composable can possess\n* **maxZoom** maximum zoom value this Composable can possess\n* **limitPan** limits pan to bounds of parent Composable. Using this flag prevents creating empty\n  space on sides or edges of parent.\n* **fling** when set to true dragging pointer builds up velocity. When last pointer leaves\n  Composable a movement invoked against friction till velocity drops down to threshold\n* **moveToBounds** when set to true if image zoom is lower than initial zoom or panned out of image\n  boundaries moves back to bounds with animation.\n* **zoomable** when set to true zoom is enabled\n* **pannable** when set to true pan is enabled\n* **rotatable** when set to true rotation is enabled\n* **clip** when set to true clips to parent bounds. Anything outside parent bounds is not drawn\n* **clipTransformToContentScale** when set true zoomable image takes borders of image drawn while\n  zooming in. [contentScale] determines whether will be empty spaces on edges of Composable\n* **zoomOnDoubleTap** lambda that returns current [ZoomLevel] and based on current level enables\n  developer to define zoom on double tap gesture\n* **enabled** lambda can be used selectively enable or disable pan and intercepting with scroll,\n  drag or lists or pagers using current zoom, pan or rotation values\n* **onGestureStart** callback to to notify gesture has started and return current ZoomData of this\n  modifier\n* **onGesture** callback to notify about ongoing gesture and return current ZoomData of this\n  modifier\n* **onGestureEnd** callback to notify that gesture finished and return current ZoomData of this\n  modifier\n\n## Modifier.animatedZoom()\n\nModifier that zooms in or out of Composable set to. This zoom modifier has option to move back to\nbounds with an animation or option to have fling gesture when user removes from screen while\nvelocity is higher than threshold to have smooth touch effect.\n\nDifference between `Modifier.enhancedZoom()` and `Modifier.animatedZoom()` is enhanced zoom\nuses Bitmap dimensions and returns a callback that returns [EnhandedZoomData] that contains\nvisible image area which is suitable for crop while `Modifier.animatedZoom()` requires\ndimensions of Composable to have valid pan limiting behavior. More suitable for zooming\nComposables while enhanced zoom is more suitable for iamge zooming.\n\n```kotlin\nfun Modifier.animatedZoom(\n    vararg keys: Any?,\n    clip: Boolean = true,\n    animatedZoomState: AnimatedZoomState,\n    enabled: (Float, Offset, Float) -\u003e Boolean = DefaultEnabled,\n    zoomOnDoubleTap: (ZoomLevel) -\u003e Float = animatedZoomState.DefaultOnDoubleTap,\n)\n```\n\n### Parameters\n\n* **keys** are used for [Modifier.pointerInput] to restart closure when any keys assigned change\n* **clip** when set to true clips to parent bounds. Anything outside parent bounds is not drawn\n* **animatedZoomState** State of the zoom that contains option to set initial, min, max zoom,\n  enabling rotation, pan or zoom\n* **zoomOnDoubleTap** lambda that returns current [ZoomLevel] and based on current level enables\n  developer to define zoom on double tap gesture\n* **enabled** lambda can be used selectively enable or disable pan and intercepting with scroll,\n  drag or lists or pagers using current zoom, pan or rotation values\n\n## AnimatedZoomState\n\nCreate and [remember] the [AnimatedZoomState] based on the currently appropriate transform\nconfiguration to allow changing pan, zoom, and rotation.\n\nAllows to change zoom, pan, translate, or get current state by calling methods on this object. To be\nhosted and passed to [Modifier.animatedZoom].\n\n```kotlin\n@Composable\nfun rememberAnimatedZoomState(\n    contentSize: DpSize = DpSize.Zero,\n    initialZoom: Float = 1f,\n    minZoom: Float = 1f,\n    maxZoom: Float = 5f,\n    fling: Boolean = true,\n    moveToBounds: Boolean = false,\n    zoomable: Boolean = true,\n    pannable: Boolean = true,\n    rotatable: Boolean = false,\n    limitPan: Boolean = true,\n    key1: Any? = Unit\n)\n```\n\n### Parameters\n\n* **contentSize** when the content that will be zoomed is not parent pass child size to bound\n  content correctly inside parent. If parent doesn't have any content this parameter is not required\n* **initialZoom** zoom set initially\n* **minZoom** minimum zoom value\n* **maxZoom** maximum zoom value\n* **fling** when set to true dragging pointer builds up velocity. When last\n* pointer leaves Composable a movement invoked against friction till velocity drops below to\n  threshold\n* **moveToBounds** when set to true if image zoom is lower than initial zoom or panned out of image\n  boundaries moves back to bounds with animation.\n* **zoomable** when set to true zoom is enabled\n* **pannable** when set to true pan is enabled\n* **rotatable** when set to true rotation is enabled\n* **limitPan** limits pan to bounds of parent Composable. Using this flag prevents creating empty\n  space on sides or edges of parent\n\n## AnimatedZoomLayout\n\nLayout that can zoom, rotate, pan its content with fling and moving back to bounds animation.\n\n```kotlin\n@Composable\nfun AnimatedZoomLayout(\n    modifier: Modifier = Modifier,\n    clip: Boolean = true,\n    initialZoom: Float = 1f,\n    minZoom: Float = 1f,\n    maxZoom: Float = 3f,\n    fling: Boolean = true,\n    moveToBounds: Boolean = false,\n    zoomable: Boolean = true,\n    pannable: Boolean = true,\n    rotatable: Boolean = false,\n    limitPan: Boolean = true,\n    enabled: (Float, Offset, Float) -\u003e Boolean = DefaultEnabled,\n    zoomOnDoubleTap: (ZoomLevel) -\u003e Float = DefaultOnDoubleTap,\n    content: @Composable () -\u003e Unit\n)\n```\n\n### Parameters\n\n* **clip** when set to true clips to parent bounds. Anything outside parent bounds is not\n* drawn\n* **minZoom** minimum zoom value\n* **maxZoom** maximum zoom value\n* **fling** when set to true dragging pointer builds up velocity. When last pointer leaves\n  Composable a movement invoked against friction till velocity drops below to threshold\n* **moveToBounds** when set to true if image zoom is lower than initial zoom or panned out of image\n  boundaries moves back to bounds with animation.\n* **zoomable** when set to true zoom is enabled\n* **pannable** when set to true pan is enabled\n* **rotatable** when set to true rotation is enabled\n* **limitPan** limits pan to bounds of parent Composable. Using this flag prevents creating empty\n  space on sides or edges of parent\n* **zoomOnDoubleTap** lambda that returns current [ZoomLevel] and based on current level enables\n  developer to define zoom on double tap gesture\n* **enabled** lambda can be used selectively enable or disable pan and intercepting with scroll,\n  drag or lists or pagers using current zoom, pan or rotation values\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmarttoolfactory%2Fcompose-zoom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmarttoolfactory%2Fcompose-zoom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmarttoolfactory%2Fcompose-zoom/lists"}