Skip to content

Commit 18ae618

Browse files
update ZoomState with double tap function and bounds
1 parent b8c8f16 commit 18ae618

File tree

2 files changed

+58
-43
lines changed

2 files changed

+58
-43
lines changed

image/src/main/java/com/smarttoolfactory/image/zoom/ZoomModifier.kt

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import androidx.compose.runtime.*
55
import androidx.compose.ui.Modifier
66
import androidx.compose.ui.composed
77
import androidx.compose.ui.draw.clipToBounds
8-
import androidx.compose.ui.geometry.Offset
98
import androidx.compose.ui.graphics.graphicsLayer
109
import androidx.compose.ui.input.pointer.pointerInput
1110
import com.smarttoolfactory.gesture.detectTransformGestures
@@ -86,10 +85,11 @@ fun Modifier.zoom(
8685

8786
zoomLevel = newZoomLevel
8887

89-
coroutineScope.run {
90-
launch { zoomState.animatePanTo(Offset.Zero) }
91-
launch { zoomState.animateZoomTo(newZoom) }
92-
launch { zoomState.animateRotationTo(zoomState.rotationInitial) }
88+
coroutineScope.launch {
89+
zoomState.onDoubleTap(
90+
zoom = newZoom,
91+
rotation = zoomState.rotationInitial
92+
)
9393
}
9494
}
9595
)
@@ -193,10 +193,11 @@ fun Modifier.zoom(
193193

194194
zoomLevel = newZoomLevel
195195

196-
coroutineScope.run {
197-
launch { zoomState.animatePanTo(Offset.Zero) }
198-
launch { zoomState.animateZoomTo(newZoom) }
199-
launch { zoomState.animateRotationTo(zoomState.rotationInitial) }
196+
coroutineScope.launch {
197+
zoomState.onDoubleTap(
198+
zoom = newZoom,
199+
rotation = zoomState.rotationInitial
200+
)
200201
}
201202
}
202203
)
@@ -300,10 +301,11 @@ fun Modifier.zoom(
300301

301302
zoomLevel = newZoomLevel
302303

303-
coroutineScope.run {
304-
launch { zoomState.animatePanTo(Offset.Zero) }
305-
launch { zoomState.animateZoomTo(newZoom) }
306-
launch { zoomState.animateRotationTo(zoomState.rotationInitial) }
304+
coroutineScope.launch {
305+
zoomState.onDoubleTap(
306+
zoom = newZoom,
307+
rotation = zoomState.rotationInitial
308+
)
307309
}
308310
}
309311
)
@@ -368,8 +370,8 @@ fun Modifier.zoom(
368370
* enabling rotation, pan or zoom and contains current [ZoomData]
369371
*/
370372
fun Modifier.zoom(
371-
key1: Any? ,
372-
key2: Any? ,
373+
key1: Any?,
374+
key2: Any?,
373375
zoomState: ZoomState,
374376
clip: Boolean = true,
375377
) = zoom(

image/src/main/java/com/smarttoolfactory/image/zoom/ZoomState.kt

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import androidx.compose.ui.Modifier
99
import androidx.compose.ui.geometry.Offset
1010
import androidx.compose.ui.unit.IntSize
1111
import kotlinx.coroutines.coroutineScope
12+
import kotlinx.coroutines.launch
1213

1314

1415
/**
@@ -160,10 +161,10 @@ open class ZoomState internal constructor(
160161
initialRotation: Float = 0f,
161162
minZoom: Float = 1f,
162163
maxZoom: Float = 5f,
163-
internal val zoomEnabled: Boolean = true,
164-
internal val panEnabled: Boolean = true,
165-
internal val rotationEnabled: Boolean = true,
166-
internal val limitPan: Boolean = false
164+
internal open val zoomEnabled: Boolean = true,
165+
internal open val panEnabled: Boolean = true,
166+
internal open val rotationEnabled: Boolean = true,
167+
internal open val limitPan: Boolean = false
167168
) {
168169

169170
internal val zoomMin = minZoom.coerceAtLeast(.5f)
@@ -195,61 +196,73 @@ open class ZoomState internal constructor(
195196
rotation = animatableRotation.value
196197
)
197198

198-
199-
open fun boundPan(lowerBound: Offset, upperBound: Offset) {
199+
open fun boundPan(lowerBound: Offset?, upperBound: Offset?) {
200200
animatablePan.updateBounds(lowerBound, upperBound)
201201
}
202202

203+
internal open fun getBounds(size: IntSize): Offset {
204+
val maxX = (size.width * (zoom - 1) / 2f).coerceAtLeast(0f)
205+
val maxY = (size.height * (zoom - 1) / 2f).coerceAtLeast(0f)
206+
return Offset(maxX, maxY)
207+
}
208+
203209
internal open suspend fun updateZoomState(
204210
size: IntSize,
205211
gesturePan: Offset,
206212
gestureZoom: Float,
207213
gestureRotate: Float = 1f,
208214
) {
209-
val zoom = (zoom * gestureZoom).coerceIn(zoomMin, zoomMax)
210-
val rotation = if (rotationEnabled) {
215+
val zoomChange = (zoom * gestureZoom).coerceIn(zoomMin, zoomMax)
216+
val rotationChange = if (rotationEnabled) {
211217
rotation + gestureRotate
212218
} else {
213219
0f
214220
}
215221

222+
snapZoomTo(zoomChange)
223+
snapRotationTo(rotationChange)
224+
216225
if (panEnabled) {
217-
val offset = pan
218-
var newOffset = offset + gesturePan.times(zoom)
226+
var panChange = pan + gesturePan.times(zoom)
219227
val boundPan = limitPan && !rotationEnabled
220228

221229
if (boundPan) {
222-
val maxX = (size.width * (zoom - 1) / 2f)
223-
.coerceAtLeast(0f)
224-
val maxY = (size.height * (zoom - 1) / 2f)
225-
.coerceAtLeast(0f)
226-
newOffset = Offset(
227-
newOffset.x.coerceIn(-maxX, maxX),
228-
newOffset.y.coerceIn(-maxY, maxY)
230+
val bound = getBounds(size)
231+
panChange = Offset(
232+
panChange.x.coerceIn(-bound.x, bound.x),
233+
panChange.y.coerceIn(-bound.y, bound.y)
229234
)
230235
}
231-
snapPanTo(newOffset)
232-
}
233-
234-
if (zoomEnabled) {
235-
snapZoomTo(zoom)
236+
snapPanTo(panChange)
236237
}
238+
}
237239

238-
if (rotationEnabled) {
239-
snapRotationTo(rotation)
240-
}
240+
internal open suspend fun onDoubleTap(
241+
pan: Offset = Offset.Zero,
242+
zoom: Float = 1f,
243+
rotation: Float = 0f
244+
) = coroutineScope {
245+
launch { animatePanTo(pan) }
246+
launch { animateZoomTo(zoom) }
247+
launch { animateRotationTo(rotation) }
241248
}
242249

243250
internal suspend fun animatePanTo(pan: Offset) = coroutineScope {
244-
animatablePan.animateTo(pan)
251+
if (panEnabled) {
252+
animatablePan.animateTo(pan)
253+
}
245254
}
246255

247256
internal suspend fun animateZoomTo(zoom: Float) = coroutineScope {
248-
animatableZoom.animateTo(zoom)
257+
if (zoomEnabled) {
258+
animatableZoom.animateTo(zoom)
259+
}
249260
}
250261

251262
internal suspend fun animateRotationTo(rotation: Float) = coroutineScope {
252-
animatableRotation.animateTo(rotation)
263+
if (rotationEnabled) {
264+
animatableRotation.animateTo(rotation)
265+
}
253266
}
254267

255268
internal suspend fun snapPanTo(offset: Offset) = coroutineScope {

0 commit comments

Comments
 (0)