Skip to content

Commit

Permalink
Merge pull request #9 from Mr-Pine/development
Browse files Browse the repository at this point in the history
Merge for Version 1.1.2
  • Loading branch information
Mr-Pine authored Apr 7, 2022
2 parents 8540c33 + d5cd4ba commit 9d2c9e9
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 36 deletions.
2 changes: 1 addition & 1 deletion zoomables/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ dependencies {

ext{
PUBLISH_GROUP_ID = 'de.mr-pine.utils'
PUBLISH_VERSION = '1.1.1'
PUBLISH_VERSION = '1.1.2'
PUBLISH_ARTIFACT_ID = 'zoomables'
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import kotlinx.coroutines.CoroutineScope
* @param contentDescription text for accessibility see [Image] for further info
* @param onSwipeLeft Optional function to run when user swipes from right to left - does nothing by default
* @param onSwipeRight Optional function to run when user swipes from left to right - does nothing by default
* @param dragGesturesEnabled A function with a [ZoomableState] scope that returns a boolean value to enable/disable dragging gestures (swiping and panning). Returns `true` by default. *Note*: For some use cases it may be required that only panning is possible. Use `{!notTransformed}` in that case
* @param onDoubleTap Optional function to run when user double taps. Zooms in by 2x when scale is currently 1 and zooms out to scale = 1 when zoomed in when null (default)
*/
@Composable
Expand All @@ -34,13 +35,15 @@ public fun ZoomableImage(
contentDescription: String? = null,
onSwipeLeft: () -> Unit = {},
onSwipeRight: () -> Unit = {},
dragGesturesEnabled: ZoomableState.() -> Boolean = { true },
onDoubleTap: ((Offset) -> Unit)? = null
) {
Zoomable(
coroutineScope = coroutineScope,
zoomableState = zoomableState,
onSwipeLeft = onSwipeLeft,
onSwipeRight = onSwipeRight,
dragGesturesEnabled = dragGesturesEnabled,
onDoubleTap = onDoubleTap
) {
Image(bitmap = bitmap, contentDescription = contentDescription, modifier = modifier)
Expand All @@ -57,6 +60,7 @@ public fun ZoomableImage(
* @param contentDescription text for accessibility see [Image] for further info
* @param onSwipeLeft Optional function to run when user swipes from right to left - does nothing by default
* @param onSwipeRight Optional function to run when user swipes from left to right - does nothing by default
* @param dragGesturesEnabled A function with a [ZoomableState] scope that returns a boolean value to enable/disable dragging gestures (swiping and panning). Returns `true` by default. *Note*: For some use cases it may be required that only panning is possible. Use `{!notTransformed}` in that case
* @param onDoubleTap Optional function to run when user double taps. Zooms in by 2x when scale is currently 1 and zooms out to scale = 1 when zoomed in when null (default)
*/
@Composable
Expand All @@ -68,13 +72,15 @@ public fun ZoomableImage(
contentDescription: String? = null,
onSwipeLeft: () -> Unit = {},
onSwipeRight: () -> Unit = {},
dragGesturesEnabled: ZoomableState.() -> Boolean = { true },
onDoubleTap: ((Offset) -> Unit)? = null
) {
Zoomable(
coroutineScope = coroutineScope,
zoomableState = zoomableState,
onSwipeLeft = onSwipeLeft,
onSwipeRight = onSwipeRight,
dragGesturesEnabled = dragGesturesEnabled,
onDoubleTap = onDoubleTap
) {
Image(
Expand All @@ -95,6 +101,7 @@ public fun ZoomableImage(
* @param contentDescription text for accessibility see [Image] for further info
* @param onSwipeLeft Optional function to run when user swipes from right to left - does nothing by default
* @param onSwipeRight Optional function to run when user swipes from left to right - does nothing by default
* @param dragGesturesEnabled A function with a [ZoomableState] scope that returns a boolean value to enable/disable dragging gestures (swiping and panning). Returns `true` by default. *Note*: For some use cases it may be required that only panning is possible. Use `{!notTransformed}` in that case
* @param onDoubleTap Optional function to run when user double taps. Zooms in by 2x when scale is currently 1 and zooms out to scale = 1 when zoomed in when null (default)
*/
@Composable
Expand All @@ -106,13 +113,15 @@ public fun ZoomableImage(
contentDescription: String? = null,
onSwipeLeft: () -> Unit = {},
onSwipeRight: () -> Unit = {},
dragGesturesEnabled: ZoomableState.() -> Boolean = { true },
onDoubleTap: ((Offset) -> Unit)? = null
) {
Zoomable(
coroutineScope = coroutineScope,
zoomableState = zoomableState,
onSwipeLeft = onSwipeLeft,
onSwipeRight = onSwipeRight,
dragGesturesEnabled = dragGesturesEnabled,
onDoubleTap = onDoubleTap
) {
Image(painter = painter, contentDescription = contentDescription, modifier = modifier)
Expand Down
19 changes: 15 additions & 4 deletions zoomables/src/main/kotlin/de/mr_pine/zoomables/ZoomableState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ import kotlin.math.sqrt
*
* @param onTransformation callback invoked when transformation occurs. The callback receives the
* change from the previous event. It's relative scale multiplier for zoom, [Offset] in pixels
* for pan and degrees for rotation. If this parameter is null the default behaviour is
* zooming, panning and rotating by the supplied changes. Rotation is kept between positive and negative 180
* for pan and degrees for rotation.
*
* @property scale The current scale as [MutableState]<[Float]>
* @property offset The current offset as [MutableState]<[Offset]>
* @property rotation The current rotation in degrees as [MutableState]<[Float]>
* @property notTransformed `true` if [scale] is `1`, [offset] is [Offset.Zero] and [rotation] is `0`
*/
public class ZoomableState(
public var scale: MutableState<Float>,
Expand All @@ -42,6 +42,12 @@ public class ZoomableState(
public val rotationBehavior: Rotation,
onTransformation: ZoomableState.(zoomChange: Float, panChange: Offset, rotationChange: Float) -> Unit
) : TransformableState {

public val notTransformed: Boolean
get() {
return scale.value in (1 - 1.0E-3f)..(1 + 1.0E-3f) && offset.value.getDistanceSquared() in -1.0E-6f..1.0E-6f && rotation.value in -1.0E-3f..1.0E-3f
}

private val transformScope: TransformScope = object : TransformScope {
override fun transformBy(zoomChange: Float, panChange: Offset, rotationChange: Float) =
onTransformation(zoomChange, panChange, rotationChange)
Expand All @@ -51,6 +57,7 @@ public class ZoomableState(

private val isTransformingState = mutableStateOf(false)


override suspend fun transform(
transformPriority: MutatePriority,
block: suspend TransformScope.() -> Unit
Expand Down Expand Up @@ -87,7 +94,11 @@ public class ZoomableState(
}
}

public suspend fun animateZoomToPosition(zoomChange: Float, position: Offset, currentComposableCenter: Offset = Offset.Zero) {
public suspend fun animateZoomToPosition(
zoomChange: Float,
position: Offset,
currentComposableCenter: Offset = Offset.Zero
) {
val offsetBuffer = offset.value

val x0 = position.x - currentComposableCenter.x
Expand Down Expand Up @@ -143,7 +154,7 @@ public class ZoomableState(
*
* @param onTransformation callback invoked when transformation occurs. The callback receives the
* change from the previous event. It's relative scale multiplier for zoom, [Offset] in pixels
* for pan and degrees for rotation. If this parameter is null the default behaviour is
* for pan and degrees for rotation. If not provided the default behaviour is
* zooming, panning and rotating by the supplied changes. Rotation is kept between positive and negative 180
*
* @return A [ZoomableState] initialized with the given [initialZoom], [initialOffset] and [initialRotation]
Expand Down
66 changes: 35 additions & 31 deletions zoomables/src/main/kotlin/de/mr_pine/zoomables/Zoomables.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,18 @@ import kotlin.math.*
*
* @param coroutineScope used for smooth asynchronous zoom/pan/rotation animations
* @param zoomableState Contains the current transform states - obtained via [rememberZoomableState]
* @param dragGesturesEnabled A function with a [ZoomableState] scope that returns a boolean value to enable/disable dragging gestures (swiping and panning). Returns `true` by default. *Note*: For some use cases it may be required that only panning is possible. Use `{!notTransformed}` in that case
* @param onSwipeLeft Optional function to run when user swipes from right to left - does nothing by default
* @param onSwipeRight Optional function to run when user swipes from left to right - does nothing by default
* @param minimumSwipeDistance Minimum distance the user has to travel on the screen for it to count as swiping
* @param onDoubleTap Optional function to run when user double taps. Zooms in by 2x to the touch point when scale is currently 1 and zooms out to scale = 1 when zoomed in when null (default)
* @param onDoubleTap Optional function to run when user double taps. Zooms in by 2x to the touch point when scale is currently 1 and zooms out to scale = 1 when zoomed in when `null` (default)
*/

@Composable
public fun Zoomable(
coroutineScope: CoroutineScope,
zoomableState: ZoomableState,
dragGesturesEnabled: ZoomableState.() -> Boolean = { true },
onSwipeLeft: () -> Unit = {},
onSwipeRight: () -> Unit = {},
minimumSwipeDistance: Int = 0,
Expand Down Expand Up @@ -198,40 +200,42 @@ public fun Zoomable(
transformEventCounter++
} while (!canceled && event.changes.fastAny { it.pressed } && relevant)

do {
awaitPointerEvent()
drag = awaitTouchSlopOrCancellation(down.id) { change, over ->
change.consumePositionChange()
overSlop = over
}
} while (drag != null && !drag.positionChangeConsumed())
if (drag != null) {
dragOffset = Offset.Zero
if (zoomableState.scale.value !in 0.92f..1.08f) {
coroutineScope.launch {
zoomableState.transform {
transformBy(1f, overSlop, 0f)
}
if (zoomableState.dragGesturesEnabled()) {
do {
awaitPointerEvent()
drag = awaitTouchSlopOrCancellation(down.id) { change, over ->
change.consumePositionChange()
overSlop = over
}
} else {
dragOffset += overSlop
}
if (drag(drag.id) {
if (zoomableState.scale.value !in 0.92f..1.08f) {
zoomableState.offset.value += it.positionChange()
} else {
dragOffset += it.positionChange()
} while (drag != null && !drag.positionChangeConsumed())
if (drag != null) {
dragOffset = Offset.Zero
if (zoomableState.scale.value !in 0.92f..1.08f) {
coroutineScope.launch {
zoomableState.transform {
transformBy(1f, overSlop, 0f)
}
}
it.consumePositionChange()
} else {
dragOffset += overSlop
}
) {
if (zoomableState.scale.value in 0.92f..1.08f) {
val offsetX = dragOffset.x
if (offsetX > minimumSwipeDistance) {
onSwipeRight()
if (drag(drag.id) {
if (zoomableState.scale.value !in 0.92f..1.08f) {
zoomableState.offset.value += it.positionChange()
} else {
dragOffset += it.positionChange()
}
it.consumePositionChange()
}
) {
if (zoomableState.scale.value in 0.92f..1.08f) {
val offsetX = dragOffset.x
if (offsetX > minimumSwipeDistance) {
onSwipeRight()

} else if (offsetX < -minimumSwipeDistance) {
onSwipeLeft()
} else if (offsetX < -minimumSwipeDistance) {
onSwipeLeft()
}
}
}
}
Expand Down

0 comments on commit 9d2c9e9

Please sign in to comment.