如何在Jetpack Compose中实现图片点击切换缩放状态的动画?
Jetpack Compose 实现点击缩放图片动画(匹配Android官方效果)
核心实现思路
通过Compose的状态管理、动画API和手势处理,实现点击图片时以点击点为中心平滑放大,再次点击恢复原始尺寸的效果,完全匹配Android官方展示的缩放动效:
- 用状态变量记录缩放状态
- 捕捉点击位置作为缩放锚点
- 结合
animateContentSize和graphicsLayer实现尺寸与缩放的同步动画
完整代码实现
import androidx.compose.animation.animateContentSize import androidx.compose.animation.spring import androidx.compose.foundation.Image import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.size import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.IntSize @Composable fun ZoomableImage( imageResId: Int, initialSize: Int, zoomedSize: Int, modifier: Modifier = Modifier ) { // 记录缩放状态 var isZoomed by remember { mutableStateOf(false) } // 记录图片自身尺寸 var imageSize by remember { mutableStateOf(IntSize.Zero) } // 记录点击位置相对于图片的坐标 var tapOffset by remember { mutableStateOf(Offset.Zero) } Box( modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { Image( painter = painterResource(id = imageResId), contentDescription = "可缩放图片", modifier = Modifier // 设置当前尺寸 .size(if (isZoomed) zoomedSize else initialSize) // 尺寸过渡动画,使用spring实现弹性效果 .animateContentSize( animationSpec = spring( dampingRatio = 0.8f, stiffness = 300f ) ) // 获取图片实际尺寸 .onGloballyPositioned { coordinates -> imageSize = coordinates.size } // 控制缩放锚点和缩放比例 .graphicsLayer { if (isZoomed) { // 计算点击位置的相对比例,作为缩放原点 val xRatio = tapOffset.x / imageSize.width val yRatio = tapOffset.y / imageSize.height transformOrigin = androidx.compose.ui.graphics.TransformOrigin(xRatio, yRatio) // 计算缩放比例 val scaleFactor = zoomedSize.toFloat() / initialSize.toFloat() scaleX = scaleFactor scaleY = scaleFactor } else { // 恢复原始缩放比例 scaleX = 1f scaleY = 1f } } // 捕捉点击手势,切换缩放状态并记录点击位置 .detectTapGestures { offset -> tapOffset = offset isZoomed = !isZoomed } ) } } // 调用示例 @Composable fun ZoomableImageDemo() { ZoomableImage( imageResId = R.drawable.your_image, initialSize = 200, zoomedSize = 400 ) }
关键细节说明
- 锚点控制:通过
graphicsLayer的transformOrigin设置缩放原点为点击位置的相对比例,确保放大/缩小始终围绕点击点进行,和官方效果一致 - 动画参数:使用
spring动画配置dampingRatio和stiffness,模拟官方的弹性过渡效果,避免生硬的线性动画 - 状态同步:
animateContentSize负责尺寸的平滑变化,graphicsLayer的缩放比例同步跟进,保证动画的一致性 - 适配扩展:如果需要全屏缩放,可将
zoomedSize替换为父布局的可用尺寸,或修改Modifier.size为fillMaxSize并调整缩放比例计算逻辑
内容的提问来源于stack exchange,提问作者Fawzi Abd-Elaal
相关产品推荐
相关产品推荐

