@@ -9,6 +9,7 @@ import androidx.compose.ui.Modifier
99import androidx.compose.ui.geometry.Offset
1010import androidx.compose.ui.unit.IntSize
1111import 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