Releases: saket/telephoto
0.19.0
0.18.0
0.17.0
Highlights
- #45, #88, #140, #150, #152, #158: Upscaled images can now be zoomed.
- #135: Zoom to a specific pixel using
ZoomFocalPoint:val imageState = rememberZoomableImageState() imageState.zoomableState.zoomTo( zoomFactor = 2f, focal = ZoomFocalPoint.moveToViewportCenter(…), // or zoomAround() )
- #141: Selectively disable zooming or panning:
ZoomableAsyncImage( model = "https://dog.ceo", contentDescription = "…", gestures = EnabledZoomGestures.ZoomOnly, // or PanOnly )
- When a small image is upscaled to fit the viewport, its starting scale can be larger than its maximum zoom limit. In such cases, the image wouldn't respond to zoom gestures.
telephotonow automatically increases the max zoom limit when this happens, ensuring the image can still be zoomed.- To disable this, pass
DynamicZoomSpec.fixed(…)torememberZoomableState(). - If you're using a custom
DoubleClickToZoomListenerlistener, review its behavior with small images.
- To disable this, pass
- The receiver of
DoubleClickToZoomListener#onDoubleClick()has changed. This API was already marked experimental. FlickToDismiss()now uses rubber banding. More on this below.
Improvements
ZoomableImage()/Modifier.zoomable- Added
DynamicZoomSpecfor lazy calculation of zoom limits using the layout info. - Improved zooming using mouse-wheel scrolls on JVM.
- Migrated to
LocalHapticFeedbackso haptics can be customized or disabled.
- Added
SubSamplingImage()- #149: Tile sizes are now consistent, making it easier to render PDFs (by @sigmabeta).
Bug fixes
- #146: Added missing
contentPaddingparam toZoomableAsyncImage()(by @xertrec). - #160: Fixed ignored overzoom effect for under-zooms (by @FooIbar).
- Fixed broken double-click zooming for images that use
ContentScale.FillBounds. zoomTo()andzoomBy()no longer crash when given invalid or out-of-bound zoom factors.
Unreleased libraries
This release also includes updates to FlickToDismiss(). While it hasn't been officially released yet, some major apps have already started using it. I plan to announce it soon, but in the meantime here are the changes:
- Improved drag physics and added a rubber banding effect. You can customize this via
rememberFlickToDismissState(). - A haptic feedback is now played when the dismiss threshold is crossed. This can customized via
LocalHapticFeedback.
0.16.0
Highlights
-
Spatial geometry types: A new system of spatial geometry makes it easy to convert coordinates between the viewport and the zoomable content's coordinate space: SpatialOffset and SpatialRect. See these recipes for their usage examples:
-
Content padding:
Modifier.zoomable()andZoomableImage()now accept acontentPaddingparameter. This is similar to usingModifier.padding, but allows the zoomable content to display and receive touch events in the padded region. This is intended to be used by image croppers. -
A new sample demonstrating how
telephotocan be used to build image croppers.
image.cropper.mp4
Other changes
- #132, #137: Support for Wasm and JS targets for
Modifier.zoomable() - Call prepareToDraw() on sub-sampled bitmaps for better performance
Bug fixes
- #139: Fixed
ConnectivityManager$TooManyRequestsExceptioninZoomableAsyncImage - Small fixes for improved state restoration
Dependency updates
- Kotlin 2.1.20
- Compose Multiplatform 1.8.0
0.15.1
0.15.0
This release introduces a new library for telephoto: Modifier.zoomablePeekOverlay(), a modifier for displaying transient overlaid zoom effect, inspired by Instagram.
AsyncImage(
modifier = Modifier.zoomablePeekOverlay(…),
model = "https://example.com/image.jpg",
contentDescription = "…",
)zoom-overlay-demo.mp4
Other changes
- Auto-enabled ultra HDR mode for compatible images
- Customizable over-zoom behavior using
OverzoomEffect(example). - #9: iOS support for
Modifier.zoomable() - #118: Added partial support for AVIF images
- #120:
SubSamplingImageSourceis no longersealed. Apps can now display custom content such as PDFs and maps usingSubSamplingImage().
Bug fixes
- #114: Fixed flicker in
ZoomableImage()when a new image is loaded - #122: Fixed a bug where images stopped loading under system stress
- #129, #110: Worked around crashes related to color spaces in Compose UI
Dependency updates
- Compose multiplatform: 1.7.1
- Compile SDK: 35
- Kotlin: 2.1.0
0.14.0
New changes
- Support for Coil 3 (
me.saket.telephoto:zoomable-image-coil3:{version}) - State restoration of zoom & pan values across orientation changes
- Zoom & pan transformations now render synchronously instead of lagging behind by a frame, eliminating flicker during layout size changes
- Added ZoomableImageState#isImageDisplayedInFullQuality
- Added new APIs for custom
ZoomableImageSourceimplementations:
Bug fixes
- #95: Removed explicit recycling of bitmap decoders
- #97: Added protection against
NaNvelocities - #99: Added handling for non-existent content URIs to prevent crashes
- #110: Added workaround for a Compose UI issue with unknown color spaces
ZoomableImage()will now display itscontentDescriptioneven if the image isn't loaded yet- Content alignment can now be updated even if the image is zoomed in
- Prevented multiple buffering of
SubSamplingImageSource.rawSource()
Deprecations
ZoomableContentLocation#size()is no longer usedSubSamplingImageState#isImageLoaded→isImageDisplayedSubSamplingImageState#isImageLoadedInFullQuality→isImageDisplayedInFullQualityZoomableState#setContentLocation()→setContentLocationSynchronously()
0.13.0
0.12.1
New changes
- Added
DoubleClickToZoomListener.cycle(), which allows setting a custom max zoom factor for double clicks.
Bug fixes
- Fixed a bug that prevented back buttons from working when
ZoomableImage()was focused.
0.12.0
New changes
- #78: Support for keyboard and mouse shortcuts (by @evant)
- #67: New APIs in
ZoomableStatefor controlling zoom from code:panBy,zoomBy, andzoomTo - #32: New
onDoubleClickparameters inZoomableImage()andModifier.zoomable()for customizing double-click behavior - #91: Reduced
minSdkto 21 (by @iwb-florien-flament) - Significantly reduced the amount of work required by a
ZoomableImageSourceby offloading the detection of bad content URIs toSubSamplingImageSource.contentUriOrNull()
Bug fixes
- #93:
NullPointerExceptionwhen an image is zoomed before it is initialized - #50:
FileNotFoundException: No content providerwhen disk caching of an image is disabled usingCache-ControlHTTP headers. - Fixed infinite reloading of images when unstable image request listener are used in
ZoomableAsyncImage(). This removes the need for usingrememberwithImageRequest.listenerandImageRequest.placeholdervalues.