Skip to content

Commit 4831654

Browse files
committed
Animation: compute the real destination coordinates
1 parent d13fc41 commit 4831654

File tree

2 files changed

+54
-17
lines changed

2 files changed

+54
-17
lines changed

app/src/main/java/org/mydomain/myscan/view/CameraScreen.kt

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,22 @@ import androidx.compose.material3.Text
5252
import androidx.compose.runtime.Composable
5353
import androidx.compose.runtime.DisposableEffect
5454
import androidx.compose.runtime.LaunchedEffect
55+
import androidx.compose.runtime.MutableState
5556
import androidx.compose.runtime.getValue
5657
import androidx.compose.runtime.mutableStateOf
5758
import androidx.compose.runtime.remember
5859
import androidx.compose.runtime.setValue
5960
import androidx.compose.ui.Alignment
6061
import androidx.compose.ui.Modifier
6162
import androidx.compose.ui.draw.scale
63+
import androidx.compose.ui.geometry.Offset
6264
import androidx.compose.ui.graphics.Color
6365
import androidx.compose.ui.graphics.asImageBitmap
66+
import androidx.compose.ui.layout.boundsInWindow
67+
import androidx.compose.ui.layout.onGloballyPositioned
6468
import androidx.compose.ui.platform.LocalConfiguration
6569
import androidx.compose.ui.platform.LocalContext
70+
import androidx.compose.ui.platform.LocalDensity
6671
import androidx.compose.ui.tooling.preview.Preview
6772
import androidx.compose.ui.unit.dp
6873
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -91,6 +96,7 @@ fun CameraScreen(
9196
) {
9297
var previewView by remember { mutableStateOf<PreviewView?>(null) }
9398
val pageIds by viewModel.pageIds.collectAsStateWithLifecycle()
99+
val thumbnailCoords = remember { mutableStateOf(Offset.Zero) }
94100

95101
val captureController = remember { CameraCaptureController() }
96102
DisposableEffect(Unit) {
@@ -124,7 +130,9 @@ fun CameraScreen(
124130
pageIds = pageIds,
125131
imageLoader = { id -> viewModel.getBitmap(id) },
126132
onPageClick = { index -> viewModel.navigateTo(Screen.FinalizeDocument(index)) },
127-
listState = listState
133+
listState = listState,
134+
onLastItemPosition =
135+
{ coords -> thumbnailCoords.value = coords.localToWindow(Offset.Zero) }
128136
)
129137
},
130138
cameraUiState = CameraUiState(pageIds.size, liveAnalysisState, captureState),
@@ -136,6 +144,7 @@ fun CameraScreen(
136144
)
137145
},
138146
onFinalizePressed = onFinalizePressed,
147+
thumbnailCoords = thumbnailCoords,
139148
)
140149
}
141150

@@ -146,6 +155,7 @@ private fun CameraScreenScaffold(
146155
cameraUiState: CameraUiState,
147156
onCapture: () -> Unit,
148157
onFinalizePressed: () -> Unit,
158+
thumbnailCoords: MutableState<Offset>,
149159
) {
150160
Box {
151161
Scaffold(
@@ -172,12 +182,12 @@ private fun CameraScreenScaffold(
172182
)
173183
}
174184
}
175-
CapturedImage(cameraUiState)
185+
CapturedImage(cameraUiState, thumbnailCoords)
176186
}
177187
}
178188

179189
@Composable
180-
private fun CapturedImage(cameraUiState: CameraUiState) {
190+
private fun CapturedImage(cameraUiState: CameraUiState, thumbnailCoords: MutableState<Offset>) {
181191
cameraUiState.captureState.processedImage?.let { image ->
182192
Surface(
183193
color = Color.Black.copy(alpha = 0.3f),
@@ -190,8 +200,9 @@ private fun CapturedImage(cameraUiState: CameraUiState) {
190200
isAnimating = true
191201
}
192202
val transition = updateTransition(targetState = isAnimating, label = "captureAnimation")
193-
val targetOffsetX = 0.dp // TODO real value
194-
val targetOffsetY = 200.dp // TODO real value
203+
val density = LocalDensity.current
204+
var targetOffsetX by remember { mutableStateOf(0.dp) }
205+
var targetOffsetY by remember { mutableStateOf(0.dp) }
195206

196207
val offsetX by transition.animateDp(
197208
transitionSpec = { tween(durationMillis = ANIMATION_DURATION) },
@@ -206,15 +217,29 @@ private fun CapturedImage(cameraUiState: CameraUiState) {
206217
label = "scale"
207218
) { if (it) 0.3f else 1f }
208219

209-
Image(
210-
bitmap = image.asImageBitmap(),
211-
contentDescription = null,
212-
modifier = Modifier
213-
.fillMaxSize()
214-
.padding(24.dp)
215-
.offset(x = offsetX, y = offsetY - 100.dp)
216-
.scale(scale)
217-
)
220+
221+
val justABitToTheTop = 100.dp
222+
Box (modifier = Modifier
223+
.onGloballyPositioned { coordinates ->
224+
val bounds = coordinates.boundsInWindow()
225+
val centerX = bounds.left + bounds.width / 2
226+
val centerY = bounds.top + bounds.height / 2
227+
with(density) {
228+
targetOffsetX = thumbnailCoords.value.x.toDp() - centerX.toDp() + PAGE_LIST_ELEMENT_SIZE_DP.dp
229+
targetOffsetY = thumbnailCoords.value.y.toDp() - centerY.toDp() + justABitToTheTop
230+
}
231+
}
232+
) {
233+
Image(
234+
bitmap = image.asImageBitmap(),
235+
contentDescription = null,
236+
modifier = Modifier
237+
.fillMaxSize()
238+
.padding(24.dp)
239+
.offset(x = offsetX, y = offsetY - justABitToTheTop)
240+
.scale(scale)
241+
)
242+
}
218243
}
219244
}
220245

@@ -333,6 +358,7 @@ fun CameraScreenPreviewWithProcessedImage() {
333358
private fun ScreenPreview(captureState: CaptureState) {
334359
val context = LocalContext.current
335360
MyScanTheme {
361+
val thumbnailCoords = remember { mutableStateOf(Offset.Zero) }
336362
CameraScreenScaffold(
337363
cameraPreview = {
338364
Box(
@@ -356,12 +382,13 @@ private fun ScreenPreview(captureState: CaptureState) {
356382
}
357383
},
358384
onPageClick = {},
359-
listState = LazyListState()
385+
listState = LazyListState(),
360386
)
361387
},
362388
cameraUiState = CameraUiState(pageCount = 4, LiveAnalysisState(), captureState),
363389
onCapture = {},
364390
onFinalizePressed = {},
391+
thumbnailCoords = thumbnailCoords,
365392
)
366393
}
367394
}

app/src/main/java/org/mydomain/myscan/view/PageList.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,20 @@ import androidx.compose.ui.Alignment
3636
import androidx.compose.ui.Modifier
3737
import androidx.compose.ui.graphics.Color
3838
import androidx.compose.ui.graphics.asImageBitmap
39+
import androidx.compose.ui.layout.LayoutCoordinates
40+
import androidx.compose.ui.layout.onGloballyPositioned
3941
import androidx.compose.ui.unit.dp
4042

43+
const val PAGE_LIST_ELEMENT_SIZE_DP = 120
44+
4145
@Composable
4246
fun CommonPageList(
4347
pageIds: List<String>,
4448
imageLoader: (String) -> Bitmap?,
4549
onPageClick: (Int) -> Unit,
4650
listState: LazyListState = rememberLazyListState(),
4751
currentPageIndex: Int? = null,
52+
onLastItemPosition: ((LayoutCoordinates) -> Unit)? = null,
4853
) {
4954
LazyRow (
5055
state = listState,
@@ -60,15 +65,20 @@ fun CommonPageList(
6065
val image = imageLoader(id)
6166
if (image != null) {
6267
val bitmap = image.asImageBitmap()
68+
6369
val isSelected = index == currentPageIndex
6470
val borderColor =
6571
if (isSelected) MaterialTheme.colorScheme.primary else Color.Transparent
66-
val maxImageSize = 120.dp
67-
val modifier =
72+
val maxImageSize = PAGE_LIST_ELEMENT_SIZE_DP.dp
73+
var modifier =
6874
if (bitmap.height > bitmap.width)
6975
Modifier.height(maxImageSize)
7076
else
7177
Modifier.width(maxImageSize)
78+
val isLastItem = index == pageIds.lastIndex
79+
if (isLastItem && onLastItemPosition != null) {
80+
modifier = modifier.onGloballyPositioned(onLastItemPosition)
81+
}
7282
Image(
7383
bitmap = bitmap,
7484
contentDescription = null,

0 commit comments

Comments
 (0)