Skip to content

Commit 6660945

Browse files
add EnhancedZoomableImage
1 parent 8247ac1 commit 6660945

File tree

1 file changed

+197
-0
lines changed

1 file changed

+197
-0
lines changed
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
package com.smarttoolfactory.image.zoom
2+
3+
import androidx.compose.foundation.Image
4+
import androidx.compose.runtime.Composable
5+
import androidx.compose.ui.Alignment
6+
import androidx.compose.ui.Modifier
7+
import androidx.compose.ui.graphics.ColorFilter
8+
import androidx.compose.ui.graphics.DefaultAlpha
9+
import androidx.compose.ui.graphics.FilterQuality
10+
import androidx.compose.ui.graphics.ImageBitmap
11+
import androidx.compose.ui.graphics.drawscope.DrawScope
12+
import androidx.compose.ui.layout.ContentScale
13+
import androidx.compose.ui.unit.IntSize
14+
import com.smarttoolfactory.image.ImageWithConstraints
15+
16+
/**
17+
* Zoomable image that zooms in and out in [ [minZoom], [maxZoom] ] interval and translates
18+
* zoomed image based on pointer position.
19+
* Double tap gestures reset image translation and zoom to default values with animation.
20+
*
21+
* @param initialZoom zoom set initially
22+
* @param minZoom minimum zoom value this Composable can possess
23+
* @param maxZoom maximum zoom value this Composable can possess
24+
* @param limitPan limits pan to bounds of parent Composable. Using this flag prevents creating
25+
* empty space on sides or edges of parent.
26+
* @param fling when set to true dragging pointer builds up velocity. When last
27+
* pointer leaves Composable a movement invoked against friction till velocity drops down
28+
* to threshold
29+
* @param moveToBounds when set to true if image zoom is lower than initial zoom or
30+
* panned out of image boundaries moves back to bounds with animation.
31+
* @param consume flag to prevent other gestures such as scroll, drag or transform to get
32+
* event propagations
33+
* @param zoomable when set to true zoom is enabled
34+
* @param pannable when set to true pan is enabled
35+
* @param rotatable when set to true rotation is enabled
36+
* @param clip when set to true clips to parent bounds. Anything outside parent bounds is not
37+
* drawn
38+
* @param clipTransformToContentScale when set true zoomable image takes borders of image drawn
39+
* while zooming in. [contentScale] determines whether will be empty spaces on edges of Composable
40+
* @param onGestureStart callback to to notify gesture has started and return current ZoomData
41+
* of this modifier
42+
* @param onGesture callback to notify about ongoing gesture and return current ZoomData
43+
* of this modifier
44+
* @param onGestureEnd callback to notify that gesture finished and return current ZoomData
45+
* of this modifier
46+
*/
47+
@Composable
48+
fun EnhancedZoomableImage(
49+
modifier: Modifier = Modifier,
50+
imageBitmap: ImageBitmap,
51+
alignment: Alignment = Alignment.Center,
52+
contentScale: ContentScale = ContentScale.Fit,
53+
contentDescription: String? = null,
54+
alpha: Float = DefaultAlpha,
55+
colorFilter: ColorFilter? = null,
56+
filterQuality: FilterQuality = DrawScope.DefaultFilterQuality,
57+
initialZoom: Float = 1f,
58+
minZoom: Float = .5f,
59+
maxZoom: Float = 5f,
60+
limitPan: Boolean = true,
61+
fling: Boolean = false,
62+
moveToBounds: Boolean = true,
63+
zoomable: Boolean = true,
64+
pannable: Boolean = true,
65+
rotatable: Boolean = false,
66+
clip: Boolean = true,
67+
clipTransformToContentScale: Boolean = false,
68+
consume: Boolean = true,
69+
onGestureStart: ((EnhancedZoomData) -> Unit)? = null,
70+
onGesture: ((EnhancedZoomData) -> Unit)? = null,
71+
onGestureEnd: ((EnhancedZoomData) -> Unit)? = null
72+
) {
73+
74+
val zoomModifier = Modifier
75+
.enhancedZoom(
76+
enhancedZoomState = rememberEnhancedZoomState(
77+
imageSize = IntSize(imageBitmap.width, imageBitmap.height),
78+
initialZoom = initialZoom,
79+
minZoom = minZoom,
80+
maxZoom = maxZoom,
81+
limitPan = limitPan,
82+
fling = fling,
83+
moveToBounds = moveToBounds,
84+
zoomable = zoomable,
85+
pannable = pannable,
86+
rotatable = rotatable
87+
),
88+
consume = consume,
89+
clip = clip,
90+
onGestureStart = onGestureStart,
91+
onGesture = onGesture,
92+
onGestureEnd = onGestureEnd
93+
)
94+
95+
ImageWithConstraints(
96+
modifier = if (clipTransformToContentScale) modifier else modifier.then(zoomModifier),
97+
imageBitmap = imageBitmap,
98+
alignment = alignment,
99+
contentScale = contentScale,
100+
contentDescription = contentDescription,
101+
alpha = alpha,
102+
colorFilter = colorFilter,
103+
filterQuality = filterQuality,
104+
drawImage = !clipTransformToContentScale
105+
) {
106+
107+
if (clipTransformToContentScale) {
108+
Image(
109+
bitmap = imageBitmap,
110+
contentScale = contentScale,
111+
modifier = zoomModifier,
112+
alignment = alignment,
113+
contentDescription = contentDescription,
114+
alpha = alpha,
115+
colorFilter = colorFilter,
116+
filterQuality = filterQuality,
117+
)
118+
}
119+
}
120+
}
121+
122+
/**
123+
* Zoomable image that zooms in and out in [zoomState.minZoom, zoomState.maxZoom] interval
124+
* and translates
125+
* zoomed image based on pointer position.
126+
* Double tap gestures reset image translation and zoom to default values with animation.
127+
*
128+
* @param clip when set to true clips to parent bounds. Anything outside parent bounds is not
129+
* drawn
130+
* @param clipTransformToContentScale when set true zoomable image takes borders of image drawn
131+
* while zooming in. [contentScale] determines whether will be empty spaces on edges of Composable
132+
* @param consume flag to prevent other gestures such as scroll, drag or transform to get
133+
* event propagations
134+
* @param onGestureStart callback to to notify gesture has started and return current ZoomData
135+
* of this modifier
136+
* @param onGesture callback to notify about ongoing gesture and return current ZoomData
137+
* of this modifier
138+
* @param onGestureEnd callback to notify that gesture finished and return current ZoomData
139+
* of this modifier
140+
*/
141+
@Composable
142+
fun EnhancedZoomableImage(
143+
modifier: Modifier = Modifier,
144+
imageBitmap: ImageBitmap,
145+
alignment: Alignment = Alignment.Center,
146+
contentScale: ContentScale = ContentScale.Fit,
147+
contentDescription: String? = null,
148+
alpha: Float = DefaultAlpha,
149+
colorFilter: ColorFilter? = null,
150+
filterQuality: FilterQuality = DrawScope.DefaultFilterQuality,
151+
clip: Boolean = true,
152+
clipTransformToContentScale: Boolean = false,
153+
zoomState: EnhancedZoomState,
154+
consume: Boolean = true,
155+
onGestureStart: ((EnhancedZoomData) -> Unit)? = null,
156+
onGesture: ((EnhancedZoomData) -> Unit)? = null,
157+
onGestureEnd: ((EnhancedZoomData) -> Unit)? = null
158+
) {
159+
160+
val zoomModifier = Modifier
161+
.enhancedZoom(
162+
enhancedZoomState = zoomState,
163+
consume = consume,
164+
clip = clip,
165+
onGestureStart = onGestureStart,
166+
onGesture = onGesture,
167+
onGestureEnd = onGestureEnd
168+
)
169+
170+
ImageWithConstraints(
171+
modifier = if (clipTransformToContentScale) modifier else modifier.then(zoomModifier),
172+
imageBitmap = imageBitmap,
173+
alignment = alignment,
174+
contentScale = contentScale,
175+
contentDescription = contentDescription,
176+
alpha = alpha,
177+
colorFilter = colorFilter,
178+
filterQuality = filterQuality,
179+
drawImage = !clipTransformToContentScale
180+
) {
181+
182+
if (clipTransformToContentScale) {
183+
Image(
184+
bitmap = imageBitmap,
185+
contentScale = contentScale,
186+
modifier = zoomModifier,
187+
alignment = alignment,
188+
contentDescription = contentDescription,
189+
alpha = alpha,
190+
colorFilter = colorFilter,
191+
filterQuality = filterQuality,
192+
)
193+
}
194+
}
195+
}
196+
197+

0 commit comments

Comments
 (0)