Use Modifier.graphicsLayer for the animation

This commit is contained in:
Pierre-Yves Nicolas
2025-06-25 21:22:57 +02:00
parent 802bf20f08
commit 5cfe6244d3

View File

@@ -19,7 +19,6 @@ import android.graphics.BitmapFactory
import android.util.Log import android.util.Log
import androidx.camera.core.ImageProxy import androidx.camera.core.ImageProxy
import androidx.camera.view.PreviewView import androidx.camera.view.PreviewView
import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition import androidx.compose.animation.core.updateTransition
@@ -36,7 +35,6 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
@@ -54,15 +52,16 @@ import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue 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.scale
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.boundsInWindow import androidx.compose.ui.layout.boundsInWindow
import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
@@ -201,23 +200,22 @@ private fun CapturedImage(cameraUiState: CameraUiState, thumbnailCoords: Mutable
} }
val transition = updateTransition(targetState = isAnimating, label = "captureAnimation") val transition = updateTransition(targetState = isAnimating, label = "captureAnimation")
val density = LocalDensity.current val density = LocalDensity.current
var targetOffsetX by remember { mutableStateOf(0.dp) } var targetOffsetX by remember { mutableFloatStateOf(0f) }
var targetOffsetY by remember { mutableStateOf(0.dp) } var targetOffsetY by remember { mutableFloatStateOf(0f) }
val offsetX by transition.animateDp( val offsetX by transition.animateFloat(
transitionSpec = { tween(durationMillis = ANIMATION_DURATION) }, transitionSpec = { tween(durationMillis = ANIMATION_DURATION) },
label = "offsetX" label = "offsetX"
) { if (it) targetOffsetX else 0.dp } ) { if (it) targetOffsetX else 0f }
val offsetY by transition.animateDp( val offsetY by transition.animateFloat(
transitionSpec = { tween(durationMillis = ANIMATION_DURATION) }, transitionSpec = { tween(durationMillis = ANIMATION_DURATION) },
label = "offsetY" label = "offsetY"
) { if (it) targetOffsetY else 0.dp } ) { if (it) targetOffsetY else 0f }
val scale by transition.animateFloat( val scale by transition.animateFloat(
transitionSpec = { tween(durationMillis = ANIMATION_DURATION) }, transitionSpec = { tween(durationMillis = ANIMATION_DURATION) },
label = "scale" label = "scale"
) { if (it) 0.3f else 1f } ) { if (it) 0.3f else 1f }
val justABitToTheTop = 100.dp val justABitToTheTop = 100.dp
Box (modifier = Modifier Box (modifier = Modifier
.onGloballyPositioned { coordinates -> .onGloballyPositioned { coordinates ->
@@ -225,8 +223,8 @@ private fun CapturedImage(cameraUiState: CameraUiState, thumbnailCoords: Mutable
val centerX = bounds.left + bounds.width / 2 val centerX = bounds.left + bounds.width / 2
val centerY = bounds.top + bounds.height / 2 val centerY = bounds.top + bounds.height / 2
with(density) { with(density) {
targetOffsetX = thumbnailCoords.value.x.toDp() - centerX.toDp() + PAGE_LIST_ELEMENT_SIZE_DP.dp targetOffsetX = thumbnailCoords.value.x - centerX + PAGE_LIST_ELEMENT_SIZE_DP.dp.toPx()
targetOffsetY = thumbnailCoords.value.y.toDp() - centerY.toDp() + justABitToTheTop targetOffsetY = thumbnailCoords.value.y - centerY + justABitToTheTop.toPx()
} }
} }
) { ) {
@@ -236,8 +234,12 @@ private fun CapturedImage(cameraUiState: CameraUiState, thumbnailCoords: Mutable
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(24.dp) .padding(24.dp)
.offset(x = offsetX, y = offsetY - justABitToTheTop) .graphicsLayer {
.scale(scale) translationX = offsetX
translationY = offsetY - justABitToTheTop.toPx()
scaleX = scale
scaleY = scale
}
) )
} }
} }