CameraScreen: animate captured image
This commit is contained in:
@@ -19,6 +19,10 @@ 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.tween
|
||||||
|
import androidx.compose.animation.core.updateTransition
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.LocalIndication
|
import androidx.compose.foundation.LocalIndication
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
@@ -32,6 +36,7 @@ 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
|
||||||
@@ -53,6 +58,7 @@ 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.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.asImageBitmap
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
@@ -73,6 +79,9 @@ data class CameraUiState(
|
|||||||
val captureState: CaptureState
|
val captureState: CaptureState
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const val CAPTURED_IMAGE_DISPLAY_DURATION = 1500L
|
||||||
|
const val ANIMATION_DURATION = 200
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CameraScreen(
|
fun CameraScreen(
|
||||||
viewModel: MainViewModel,
|
viewModel: MainViewModel,
|
||||||
@@ -91,7 +100,7 @@ fun CameraScreen(
|
|||||||
val captureState by viewModel.captureState.collectAsStateWithLifecycle()
|
val captureState by viewModel.captureState.collectAsStateWithLifecycle()
|
||||||
if (captureState.isProcessed()) {
|
if (captureState.isProcessed()) {
|
||||||
LaunchedEffect(captureState) {
|
LaunchedEffect(captureState) {
|
||||||
delay(1500)
|
delay(CAPTURED_IMAGE_DISPLAY_DURATION)
|
||||||
viewModel.addProcessedImage()
|
viewModel.addProcessedImage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,6 +147,7 @@ private fun CameraScreenScaffold(
|
|||||||
onCapture: () -> Unit,
|
onCapture: () -> Unit,
|
||||||
onFinalizePressed: () -> Unit,
|
onFinalizePressed: () -> Unit,
|
||||||
) {
|
) {
|
||||||
|
Box {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
CameraScreenFooter(
|
CameraScreenFooter(
|
||||||
@@ -147,7 +157,11 @@ private fun CameraScreenScaffold(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
Box(modifier = Modifier.padding(bottom = innerPadding.calculateBottomPadding()).fillMaxSize()) {
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(bottom = innerPadding.calculateBottomPadding())
|
||||||
|
.fillMaxSize()
|
||||||
|
) {
|
||||||
CameraPreviewWithOverlay(cameraPreview, cameraUiState)
|
CameraPreviewWithOverlay(cameraPreview, cameraUiState)
|
||||||
MessageBox(cameraUiState.liveAnalysisState.inferenceTime)
|
MessageBox(cameraUiState.liveAnalysisState.inferenceTime)
|
||||||
CaptureButton(
|
CaptureButton(
|
||||||
@@ -156,20 +170,52 @@ private fun CameraScreenScaffold(
|
|||||||
.align(Alignment.BottomCenter)
|
.align(Alignment.BottomCenter)
|
||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
)
|
)
|
||||||
cameraUiState.captureState.processedImage?.let {
|
}
|
||||||
|
}
|
||||||
|
CapturedImage(cameraUiState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun CapturedImage(cameraUiState: CameraUiState) {
|
||||||
|
cameraUiState.captureState.processedImage?.let { image ->
|
||||||
Surface(
|
Surface(
|
||||||
color = Color.Black.copy(alpha = 0.3f),
|
color = Color.Black.copy(alpha = 0.3f),
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize(),
|
||||||
)
|
) {}
|
||||||
{}
|
|
||||||
|
var isAnimating by remember { mutableStateOf(false) }
|
||||||
|
LaunchedEffect(image) {
|
||||||
|
delay(CAPTURED_IMAGE_DISPLAY_DURATION - ANIMATION_DURATION)
|
||||||
|
isAnimating = true
|
||||||
|
}
|
||||||
|
val transition = updateTransition(targetState = isAnimating, label = "captureAnimation")
|
||||||
|
val targetOffsetX = 0.dp // TODO real value
|
||||||
|
val targetOffsetY = 200.dp // TODO real value
|
||||||
|
|
||||||
|
val offsetX by transition.animateDp(
|
||||||
|
transitionSpec = { tween(durationMillis = ANIMATION_DURATION) },
|
||||||
|
label = "offsetX"
|
||||||
|
) { if (it) targetOffsetX else 0.dp }
|
||||||
|
val offsetY by transition.animateDp(
|
||||||
|
transitionSpec = { tween(durationMillis = ANIMATION_DURATION) },
|
||||||
|
label = "offsetY"
|
||||||
|
) { if (it) targetOffsetY else 0.dp }
|
||||||
|
val scale by transition.animateFloat(
|
||||||
|
transitionSpec = { tween(durationMillis = ANIMATION_DURATION) },
|
||||||
|
label = "scale"
|
||||||
|
) { if (it) 0.3f else 1f }
|
||||||
|
|
||||||
Image(
|
Image(
|
||||||
bitmap = it.asImageBitmap(),
|
bitmap = image.asImageBitmap(),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier.fillMaxSize().padding(24.dp)
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(24.dp)
|
||||||
|
.offset(x = offsetX, y = offsetY - 100.dp)
|
||||||
|
.scale(scale)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
Reference in New Issue
Block a user