zoomable

repository·main·Indexed 20 days ago

https://github.com/usuiat/zoomable

A Compose Multiplatform library providing pinch-to-zoom, double-tap, and mouse wheel zooming for composable components across Android, iOS, Desktop, and Wasm. It includes features such as Modifier.zoomable, snap-back zoom, and experimental support for scrollable components via Modifier.zoomableWithScroll.

Tokens
1.5K
Snippets
6
Records
10
Agent score
23%

What's inside zoomable

  1. Configure Scroll Gesture Propagation

    main

    When using Zoomable inside a scrollable parent (like a Pager), you can control when scroll gestures are passed to that parent using the scrollGesturePropagation parameter:

    • ScrollGesturePropagation.ContentEdge: Gestures propagate to the parent only when the content is at its edge and the user attempts to scroll further.
    • ScrollGesturePropagation.NotZoomed: Gestures propagate to the parent only when the content is not currently zoomed in.
  2. Install Zoomable via Maven Central

    main

    Add Maven Central to your repositories and include the zoomable dependency in your build.gradle file. Replace $version with the latest version available on Maven Central.

    repositories {
        mavenCentral()
    }
    
    dependencies {
        implementation "net.engawapg.lib:zoomable:$version"
    }
  3. Basic Usage of Modifier.zoomable

    main

    To make a composable (like an Image) zoomable, apply the Modifier.zoomable extension function. You must provide a ZoomState created via rememberZoomState(). If you know the content size beforehand, passing it to rememberZoomState(contentSize = ...) optimizes the offset range.

    val painter = painterResource(id = R.drawable.penguin)
    val zoomState = rememberZoomState(contentSize = painter.intrinsicSize)
    Image(
        painter = painter,
        contentDescription = "Zoomable image",
        contentScale = ContentScale.Fit,
        modifier = Modifier
            .fillMaxSize()
            .zoomable(zoomState),
    )
  4. Use Zoomable with Asynchronous Image Loading (Coil)

    main

    When using asynchronous image loading, you must ensure the ZoomState knows the content size once the image is loaded to ensure correct panning/offsetting.

    If using rememberAsyncImagePainter, pass the intrinsicSize to rememberZoomState immediately if available.

    If using AsyncImage, call zoomState.setContentSize() inside the onSuccess callback.

    val zoomState = rememberZoomState()
    AsyncImage(
        model = "https://example.com/image.jpg",
        contentDescription = "Zoomable image",
        contentScale = ContentScale.Fit,
        onSuccess = { state ->
            zoomState.setContentSize(state.painter.intrinsicSize)
        },
        modifier = Modifier
            .fillMaxSize()
            .zoomable(zoomState),
    )
  5. Use Snap Back Zoom

    main
    Use Modifier.snapBackZoomable to create an effect where the content automatically returns to its original scale/size when the user releases their fingers after a pinch gesture. This is useful for Instagram-like behavior.
  6. Zoom with Scrollable Components (Experimental)

    main

    To apply zoom functionality to components that are already scrollable (like LazyColumn, LazyRow, or a Column with verticalScroll), use the experimental Modifier.zoomableWithScroll.

    Important: If using standard scroll modifiers (like horizontalScroll), place zoomableWithScroll before the scroll modifier in the chain.

    // For LazyColumn
    LazyColumn(
        modifier = Modifier.zoomableWithScroll(rememberZoomState())
    ) {
        items(100) { Text("Item $it") }
    }
    
    // For Column with horizontalScroll
    Column(
        modifier = Modifier
            .fillMaxSize()
            .zoomableWithScroll(rememberZoomState())
            .horizontalScroll(rememberScrollState())
    ) {
        repeat(100) { Text("Item $it") }
    }
  7. Configure Mouse Scroll Wheel Behavior

    main

    Zoomable supports mouse wheel zooming. By default, this requires holding the Ctrl key. You can customize this via the mouseWheelZoom parameter:

    • Disabled: Disables mouse wheel zoom.
    • Enabled: Enables zoom via mouse wheel without requiring any modifier keys.
    • EnabledWithCtrlKey, EnabledWithShiftKey, EnabledWithAltKey, EnabledWithMetaKey: Enables zoom only when the specified modifier key is held.
  8. Configure One Finger Zoom

    main

    The one-finger zoom action (a tap followed by a vertical drag) is enabled by default. To disable this behavior, set enableOneFingerZoom = false in the zoomable modifier.

    zoomable(
        zoomState = zoomState,
        enableOneFingerZoom = false,
    )
  9. Customize Double Tap Action

    main

    By default, a double tap toggles the scale between 1.0f and 2.5f. You can customize this behavior using the onDoubleTap callback in Modifier.zoomable.

    • Toggle to a specific scale: Use zoomState.toggleScale(targetScale, position).
    • Custom logic (e.g., stepped zoom): Use zoomState.changeScale(targetScale, position) to implement custom scaling steps.
    • Disable double tap: Pass an empty lambda {} to onDoubleTap.
    // Example: Stepped zoom logic
    zoomable(
        zoomState = zoomState,
        onDoubleTap = { position ->
            val targetScale = when {
                zoomState.scale < 2f -> 2f
                zoomState.scale < 4f -> 4f
                else -> 1f
            }
            zoomState.changeScale(targetScale, position)
        }
    )