Fix image zoom focal point
This commit is contained in:
1 parent
62cb6c91d5
commit
0b4da64062
2 files changed
+67
-5
No files matched your search
@@ -23,6 +23,7 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.FilterQuality
|
import androidx.compose.ui.graphics.FilterQuality
|
||||||
import androidx.compose.ui.graphics.ImageBitmap
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
@@ -242,17 +243,28 @@ private fun ZoomableImage(image: ImageBitmap) {
|
|||||||
modifier =
|
modifier =
|
||||||
Modifier.fillMaxSize()
|
Modifier.fillMaxSize()
|
||||||
.pointerInput(Unit) {
|
.pointerInput(Unit) {
|
||||||
detectTransformGestures { _, pan, zoom, _ ->
|
detectTransformGestures { centroid, pan, zoom, _ ->
|
||||||
// Floor of 1 so the image cannot be pinched smaller than fitted, which is
|
// Floor of 1 so the image cannot be pinched smaller than fitted, which is
|
||||||
// already the whole of it; a ceiling so it cannot be lost off-screen.
|
// already the whole of it; a ceiling so it cannot be lost off-screen.
|
||||||
scale = (scale * zoom).coerceIn(1f, 8f)
|
val oldScale = scale
|
||||||
if (scale > 1f) {
|
val newScale = (oldScale * zoom).coerceIn(1f, 8f)
|
||||||
offsetX += pan.x
|
if (newScale > 1f) {
|
||||||
offsetY += pan.y
|
val offset =
|
||||||
|
zoomOffset(
|
||||||
|
Offset(offsetX, offsetY),
|
||||||
|
centroid,
|
||||||
|
pan,
|
||||||
|
oldScale,
|
||||||
|
newScale,
|
||||||
|
Offset(size.width / 2f, size.height / 2f),
|
||||||
|
)
|
||||||
|
offsetX = offset.x
|
||||||
|
offsetY = offset.y
|
||||||
} else {
|
} else {
|
||||||
offsetX = 0f
|
offsetX = 0f
|
||||||
offsetY = 0f
|
offsetY = 0f
|
||||||
}
|
}
|
||||||
|
scale = newScale
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.graphicsLayer {
|
.graphicsLayer {
|
||||||
@@ -263,3 +275,16 @@ private fun ZoomableImage(image: ImageBitmap) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Keeps the image point beneath [centroid] beneath the fingers as its scale changes. */
|
||||||
|
internal fun zoomOffset(
|
||||||
|
offset: Offset,
|
||||||
|
centroid: Offset,
|
||||||
|
pan: Offset,
|
||||||
|
oldScale: Float,
|
||||||
|
newScale: Float,
|
||||||
|
viewportCenter: Offset,
|
||||||
|
): Offset {
|
||||||
|
val scaleChange = newScale / oldScale
|
||||||
|
return offset * scaleChange + (centroid - viewportCenter) * (1f - scaleChange) + pan
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.example.aiapp
|
||||||
|
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class SessionImageTest {
|
||||||
|
@Test
|
||||||
|
fun `zoom keeps the region panned to in the center`() {
|
||||||
|
assertEquals(
|
||||||
|
Offset(240f, -160f),
|
||||||
|
zoomOffset(
|
||||||
|
offset = Offset(120f, -80f),
|
||||||
|
centroid = Offset(500f, 1000f),
|
||||||
|
pan = Offset.Zero,
|
||||||
|
oldScale = 2f,
|
||||||
|
newScale = 4f,
|
||||||
|
viewportCenter = Offset(500f, 1000f),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `zoom keeps an off-center pinch beneath moving fingers`() {
|
||||||
|
assertEquals(
|
||||||
|
Offset(460f, 420f),
|
||||||
|
zoomOffset(
|
||||||
|
offset = Offset(100f, -100f),
|
||||||
|
centroid = Offset(250f, 400f),
|
||||||
|
pan = Offset(10f, 20f),
|
||||||
|
oldScale = 2f,
|
||||||
|
newScale = 4f,
|
||||||
|
viewportCenter = Offset(500f, 1000f),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user