Skip to content

Commit 14fc32f

Browse files
change pan from Offset to separate x,y axes
Fling gesture stops if one of the x or y values of Offset reach lower or upper bound to prevent that create 2 separate Animatables to have fling gesture individually.
1 parent 05f01a9 commit 14fc32f

2 files changed

Lines changed: 50 additions & 22 deletions

File tree

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

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import androidx.compose.ui.unit.IntSize
1111
import com.smarttoolfactory.image.util.coerceIn
1212
import com.smarttoolfactory.image.util.getCropRect
1313
import kotlinx.coroutines.coroutineScope
14+
import kotlinx.coroutines.launch
1415

1516
/**
1617
* * State of the enhanced zoom that uses animations and fling
@@ -72,7 +73,7 @@ open class EnhancedZoomState constructor(
7273
val enhancedZoomData: EnhancedZoomData
7374
get() = EnhancedZoomData(
7475
zoom = animatableZoom.targetValue,
75-
pan = animatablePan.targetValue,
76+
pan = Offset(animatablePanX.targetValue, animatablePanY.targetValue),
7677
rotation = animatableRotation.targetValue,
7778
imageRegion = rectDraw,
7879
visibleRegion = calculateRectBounds()
@@ -83,7 +84,8 @@ open class EnhancedZoomState constructor(
8384
val width = size.width
8485
val height = size.height
8586
val zoom = animatableZoom.targetValue
86-
val pan = animatablePan.targetValue
87+
val panX = animatablePanX.targetValue
88+
val panY = animatablePanY.targetValue
8789

8890
// Offset for interpolating offset from (imageWidth/2,-imageWidth/2) interval
8991
// to (0, imageWidth) interval when
@@ -93,9 +95,9 @@ open class EnhancedZoomState constructor(
9395

9496
val bounds = getBounds()
9597

96-
val offsetX = (horizontalCenterOffset - pan.x.coerceIn(-bounds.x, bounds.x))
98+
val offsetX = (horizontalCenterOffset - panX.coerceIn(-bounds.x, bounds.x))
9799
.coerceAtLeast(0f) / zoom
98-
val offsetY = (verticalCenterOffset - pan.y.coerceIn(-bounds.y, bounds.y))
100+
val offsetY = (verticalCenterOffset - panY.coerceIn(-bounds.y, bounds.y))
99101
.coerceAtLeast(0f) / zoom
100102

101103
val offset = Offset(offsetX, offsetY)
@@ -203,16 +205,27 @@ open class BaseEnhancedZoomState constructor(
203205
* Create a fling gesture when user removes finger from scree to have continuous movement
204206
* until [velocityTracker] speed reached to lower bound
205207
*/
206-
private suspend fun fling() {
208+
private suspend fun fling() = coroutineScope {
207209
val velocityTracker = velocityTracker.calculateVelocity()
208210
val velocity = Offset(velocityTracker.x, velocityTracker.y)
209211

210-
animatablePan.animateDecay(
211-
velocity,
212-
exponentialDecay(
213-
absVelocityThreshold = 20f
212+
launch {
213+
animatablePanX.animateDecay(
214+
velocity.x,
215+
exponentialDecay(
216+
absVelocityThreshold = 20f
217+
)
214218
)
215-
)
219+
}
220+
221+
launch {
222+
animatablePanY.animateDecay(
223+
velocity.y,
224+
exponentialDecay(
225+
absVelocityThreshold = 20f
226+
)
227+
)
228+
}
216229
}
217230

218231
private fun resetTracking() {

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

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.smarttoolfactory.image.zoom
22

33
import androidx.compose.animation.core.Animatable
4-
import androidx.compose.animation.core.VectorConverter
54
import androidx.compose.runtime.Stable
65
import androidx.compose.ui.Modifier
76
import androidx.compose.ui.geometry.Offset
@@ -38,7 +37,8 @@ open class ZoomState(
3837
internal val zoomInitial = initialZoom.coerceIn(zoomMin, zoomMax)
3938
internal val rotationInitial = initialRotation % 360
4039

41-
internal val animatablePan = Animatable(Offset.Zero, Offset.VectorConverter)
40+
internal val animatablePanX = Animatable(0f)
41+
internal val animatablePanY = Animatable(0f)
4242
internal val animatableZoom = Animatable(zoomInitial)
4343
internal val animatableRotation = Animatable(rotationInitial)
4444

@@ -49,7 +49,7 @@ open class ZoomState(
4949
}
5050

5151
val pan: Offset
52-
get() = animatablePan.value
52+
get() = Offset(animatablePanX.value, animatablePanY.value)
5353

5454
val zoom: Float
5555
get() = animatableZoom.value
@@ -61,7 +61,7 @@ open class ZoomState(
6161
get() = animatableZoom.isRunning
6262

6363
val isPanning: Boolean
64-
get() = animatablePan.isRunning
64+
get() = animatablePanX.isRunning || animatablePanY.isRunning
6565

6666
val isRotating: Boolean
6767
get() = animatableRotation.isRunning
@@ -70,7 +70,8 @@ open class ZoomState(
7070
get() = isZooming || isPanning || isRotating
7171

7272
internal open fun updateBounds(lowerBound: Offset?, upperBound: Offset?) {
73-
animatablePan.updateBounds(lowerBound, upperBound)
73+
animatablePanX.updateBounds(lowerBound?.x, upperBound?.x)
74+
animatablePanY.updateBounds(lowerBound?.y, upperBound?.y)
7475
}
7576

7677
/**
@@ -86,7 +87,7 @@ open class ZoomState(
8687
/**
8788
* Get bounds of Composables that can be panned based on zoom level using [size]
8889
*/
89-
protected fun getBounds(): Offset {
90+
protected open fun getBounds(): Offset {
9091
return getBounds(size)
9192
}
9293

@@ -114,7 +115,8 @@ open class ZoomState(
114115
val bound = getBounds(size)
115116
updateBounds(bound.times(-1f), bound)
116117
}
117-
snapPanTo(newPan)
118+
snapPanXto(newPan.x)
119+
snapPanYto(newPan.y)
118120
}
119121
}
120122

@@ -126,14 +128,21 @@ open class ZoomState(
126128
zoom: Float = 1f,
127129
rotation: Float = 0f
128130
) = coroutineScope {
129-
launch { animatePanTo(pan) }
131+
launch { animatePanXto(pan.x) }
132+
launch { animatePanYto(pan.y) }
130133
launch { animateZoomTo(zoom) }
131134
launch { animateRotationTo(rotation) }
132135
}
133136

134-
internal suspend fun animatePanTo(pan: Offset) {
137+
internal suspend fun animatePanXto(panX: Float) {
135138
if (pannable) {
136-
animatablePan.animateTo(pan)
139+
animatablePanX.animateTo(panX)
140+
}
141+
}
142+
143+
internal suspend fun animatePanYto(panY: Float) {
144+
if (pannable) {
145+
animatablePanY.animateTo(panY)
137146
}
138147
}
139148

@@ -149,9 +158,15 @@ open class ZoomState(
149158
}
150159
}
151160

152-
internal suspend fun snapPanTo(offset: Offset) {
161+
internal suspend fun snapPanXto(panX:Float) {
162+
if (pannable) {
163+
animatablePanX.snapTo(panX)
164+
}
165+
}
166+
167+
internal suspend fun snapPanYto(panY:Float) {
153168
if (pannable) {
154-
animatablePan.snapTo(offset)
169+
animatablePanY.snapTo(panY)
155170
}
156171
}
157172

0 commit comments

Comments
 (0)