Skip to content

Commit eb5d025

Browse files
add ZoomLevel and ZoomState
1 parent fe675c8 commit eb5d025

File tree

3 files changed

+133
-0
lines changed

3 files changed

+133
-0
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.smarttoolfactory.image.zoom
2+
3+
import androidx.compose.ui.Modifier
4+
5+
/**
6+
* Enum class for zoom levels to use with [Modifier.zoom]
7+
*/
8+
enum class ZoomLevel {
9+
Initial, Min, Max
10+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package com.smarttoolfactory.image.zoom
2+
3+
import androidx.compose.animation.core.Animatable
4+
import androidx.compose.animation.core.VectorConverter
5+
import androidx.compose.runtime.Composable
6+
import androidx.compose.runtime.remember
7+
import androidx.compose.ui.geometry.Offset
8+
import kotlinx.coroutines.coroutineScope
9+
10+
@Composable
11+
fun rememberZoomState(
12+
initialZoom: Float = 1f,
13+
initialRotation: Float = 0f,
14+
minZoom: Float = 1f,
15+
maxZoom: Float = 5f
16+
): ZoomState {
17+
return remember {
18+
ZoomState(
19+
initialZoom = initialZoom,
20+
initialRotation = initialRotation,
21+
minZoom = minZoom,
22+
maxZoom = maxZoom
23+
)
24+
}
25+
}
26+
27+
class ZoomState internal constructor(
28+
initialZoom: Float = 1f,
29+
initialRotation: Float = 0f,
30+
minZoom: Float = 1f,
31+
maxZoom: Float = 5f
32+
) {
33+
internal var zoomLevel = ZoomLevel.Min
34+
35+
internal val zoomMin = minZoom.coerceAtLeast(.5f)
36+
internal val zoomMax = maxZoom.coerceAtLeast(1f)
37+
internal val zoomInitial = initialZoom.coerceIn(zoomMin, zoomMax)
38+
internal val rotationInitial = initialRotation % 360
39+
40+
internal val animatablePan = Animatable(Offset.Zero, Offset.VectorConverter)
41+
internal val animatableZoom = Animatable(zoomInitial)
42+
internal val animatableRotation = Animatable(rotationInitial)
43+
44+
init {
45+
require(zoomMax >= zoomMin)
46+
}
47+
48+
val pan: Offset
49+
get() = animatablePan.value
50+
51+
val zoom: Float
52+
get() = animatableZoom.value
53+
54+
val rotation: Float
55+
get() = animatableRotation.value
56+
57+
val zoomData: ZoomData
58+
get() = ZoomData(
59+
zoom = animatableZoom.value,
60+
pan = animatablePan.value,
61+
rotation = animatableRotation.value
62+
)
63+
64+
65+
fun boundPan(maxX: Float, maxY: Float) {
66+
animatablePan.updateBounds(
67+
Offset(-maxX, -maxY),
68+
Offset(maxX, maxY)
69+
)
70+
}
71+
72+
suspend fun animatePanTo(pan: Offset) = coroutineScope {
73+
animatablePan.animateTo(pan)
74+
}
75+
76+
suspend fun animateZoomTo(zoom: Float) = coroutineScope {
77+
animatableZoom.animateTo(zoom)
78+
}
79+
80+
suspend fun animateRotationTo(rotation: Float) = coroutineScope {
81+
animatableRotation.animateTo(rotation)
82+
}
83+
84+
suspend fun snapPanTo(offset: Offset) = coroutineScope {
85+
animatablePan.snapTo(offset)
86+
}
87+
88+
suspend fun snapZoomTo(zoom: Float) = coroutineScope {
89+
animatableZoom.snapTo(zoom)
90+
}
91+
92+
suspend fun snapRotationTo(rotation: Float) = coroutineScope {
93+
animatableRotation.snapTo(rotation)
94+
}
95+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.smarttoolfactory.image.zoom
2+
3+
fun calculateZoom(
4+
zoomLevel: ZoomLevel,
5+
initial: Float,
6+
min: Float,
7+
max: Float
8+
): Pair<ZoomLevel, Float> {
9+
10+
val newZoomLevel: ZoomLevel
11+
val newZoom: Float
12+
13+
when (zoomLevel) {
14+
ZoomLevel.Initial -> {
15+
newZoomLevel = ZoomLevel.Max
16+
newZoom = max
17+
}
18+
ZoomLevel.Max -> {
19+
newZoomLevel = ZoomLevel.Min
20+
newZoom = if(min == initial) (min +max)/2 else min
21+
}
22+
else -> {
23+
newZoomLevel = ZoomLevel.Initial
24+
newZoom = initial
25+
}
26+
}
27+
return Pair(newZoomLevel, newZoom)
28+
}

0 commit comments

Comments
 (0)