我正在尝试在 Jetpack Compose 中创建一个可缩放图像。我已启用放大/缩小功能,但我不确定应该如何设置 translationX 属性的限制,以便我无法将图像水平移动到框边界之外?有什么解决办法吗?
示例:
@Composable
fun ZoomableImage(
painter: Painter
) {
val scale = remember { mutableStateOf(1f) }
var offsetX by remember { mutableStateOf(0f) }
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colors.welcomeScreenBackgroundColor)
.pointerInput(Unit) {
detectTransformGestures { centroid, pan, zoom, rotation ->
scale.value *= zoom
}
},
contentAlignment = Alignment.Center
) {
Image(
modifier = Modifier
.pointerInput(Unit) {
detectHorizontalDragGestures { change, dragAmount ->
offsetX += dragAmount
}
}
.graphicsLayer(
translationX = offsetX,
scaleX = maxOf(1f, minOf(3f, scale.value)),
scaleY = maxOf(1f, minOf(3f, scale.value))
),
contentDescription = "Image",
painter = painter,
contentScale = ContentScale.Fit
)
}
}
我不确定这是否是一个好主意,但你可以使用
onPlace
。
还有一个 WIP 示例:
@OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class)
@Composable
fun ZoomableImage(
painter: Painter,
contentDescription: String?,
modifier: Modifier = Modifier,
onLongPress: ((Offset) -> Unit)? = null,
onTap: ((Offset) -> Unit)? = null
) {
val scope = rememberCoroutineScope()
var layout: LayoutCoordinates? = null
var scale by remember { mutableStateOf(1f) }
var translation by remember { mutableStateOf(Offset.Zero) }
val transformableState = rememberTransformableState { zoomChange, panChange, _ ->
scale *= zoomChange
translation += panChange.times(scale)
}
Box(
modifier = modifier
.clipToBounds()
.transformable(state = transformableState)
.pointerInput(Unit) {
detectTapGestures(
onLongPress = onLongPress,
onDoubleTap = {
val maxScale = 2f
val midScale = 1.5f
val minScale = 1f
val targetScale = when {
scale >= minScale -> midScale
scale >= midScale -> maxScale
scale >= maxScale -> minScale
else -> minScale
}
scope.launch {
transformableState.animateZoomBy(targetScale / scale)
}
},
onTap = onTap
)
}
.pointerInput(Unit) {
forEachGesture {
awaitPointerEventScope {
val down = awaitFirstDown(requireUnconsumed = false)
drag(down.id) {
if (layout == null) return@drag
val maxX = layout!!.size.width * (scale - 1) / 2f
val maxY = layout!!.size.height * (scale - 1) / 2f
val targetTranslation = (it.positionChange() + translation)
if (targetTranslation.x > -maxX && targetTranslation.x < maxX &&
targetTranslation.y > -maxY && targetTranslation.y < maxY
) {
translation = targetTranslation
it.consumePositionChange()
}
}
}
}
}
) {
Image(
painter = painter,
contentDescription = contentDescription,
modifier = Modifier
.matchParentSize()
.onPlaced { layout = it }
.graphicsLayer(
scaleX = scale,
scaleY = scale,
translationX = translation.x,
translationY = translation.y
),
contentScale = ContentScale.Fit
)
LaunchedEffect(transformableState.isTransformInProgress) {
if (!transformableState.isTransformInProgress) {
if (scale < 1f) {
val originScale = scale
val originTranslation = translation
AnimationState(initialValue = 0f).animateTo(
1f,
SpringSpec(stiffness = Spring.StiffnessLow)
) {
scale = originScale + (1 - originScale) * this.value
translation = originTranslation * (1 - this.value)
}
} else {
if (layout == null) return@LaunchedEffect
val maxX = layout!!.size.width * (scale - 1) / 2f
val maxY = layout!!.size.height * (scale - 1) / 2f
val target = Offset(
translation.x.coerceIn(-maxX, maxX),
translation.y.coerceIn(-maxY, maxY)
)
AnimationState(
typeConverter = Offset.VectorConverter,
initialValue = translation
).animateTo(target, SpringSpec(stiffness = Spring.StiffnessLow)) {
translation = this.value
}
}
}
}
}
}
如果您只需要限制图像内的平移而不是动画回到所需的级别,那就非常简单。使用可组合大小和缩放级别来约束它
val maxX = (size.width * (zoom - 1) / 2f)
val maxY = (size.height * (zoom - 1) / 2f)
offset = newOffset
offset = Offset(
newOffset.x.coerceIn(-maxX, maxX),
newOffset.y.coerceIn(-maxY, maxY)
-max, +max 是范围,当图形层的 TransformOrigin 为 (0.5,0.5) (默认值)时,您可以将其设置在 -max 到 +max 之间。更改 TransformOrigin 时请注意移动范围
全面实施
州
var zoom by remember { mutableStateOf(1f) }
var offset by remember { mutableStateOf(Offset.Zero) }
var size by remember { mutableStateOf(IntSize.Zero) }
修改器
val imageModifier = Modifier
.fillMaxSize()
.aspectRatio(4/3f)
.clipToBounds()
.pointerInput(Unit) {
detectTransformGestures(
onGesture = { _, gesturePan, gestureZoom, _ ->
val newScale = (zoom * gestureZoom).coerceIn(1f, 3f)
val newOffset = offset + gesturePan
zoom = newScale
val maxX = (size.width * (zoom - 1) / 2f)
val maxY = (size.height * (zoom - 1) / 2f)
offset = Offset(
newOffset.x.coerceIn(-maxX, maxX),
newOffset.y.coerceIn(-maxY, maxY)
)
}
)
}
.onSizeChanged {
size = it
}
.graphicsLayer {
translationX = offset.x
translationY = offset.y
scaleX = zoom
scaleY = zoom
}
我已经将图像缩放到适合高度,如何将图像水平居中(使用graphicsLayer)。你能帮助我吗?。谢谢你。