Зум и пролистывание
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,7 +3,11 @@ package core.presentation.components
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.gestures.calculatePan
|
||||
import androidx.compose.foundation.gestures.calculateZoom
|
||||
import androidx.compose.foundation.gestures.detectTransformGestures
|
||||
import androidx.compose.foundation.gestures.awaitEachGesture
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.gestures.rememberTransformableState
|
||||
import androidx.compose.foundation.gestures.transformable
|
||||
import androidx.compose.foundation.layout.*
|
||||
@@ -22,6 +26,7 @@ import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
@@ -43,6 +48,7 @@ fun AppMediaLightbox(
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val pagerState = rememberPagerState(initialPage = initialIndex, pageCount = { mediaList.size })
|
||||
var canScroll by remember { mutableStateOf(true) }
|
||||
|
||||
Dialog(
|
||||
onDismissRequest = onClose,
|
||||
@@ -60,7 +66,8 @@ fun AppMediaLightbox(
|
||||
state = pagerState,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
pageSpacing = 16.dp,
|
||||
beyondBoundsPageCount = 1
|
||||
beyondBoundsPageCount = 1,
|
||||
userScrollEnabled = canScroll
|
||||
) { page ->
|
||||
val media = mediaList[page]
|
||||
|
||||
@@ -79,22 +86,78 @@ fun AppMediaLightbox(
|
||||
} else if (media.type.startsWith("image") || media.type.contains("gif")) {
|
||||
var scale by remember { mutableFloatStateOf(1f) }
|
||||
var offset by remember { mutableStateOf(androidx.compose.ui.geometry.Offset.Zero) }
|
||||
|
||||
// Reset zoom when page changes
|
||||
LaunchedEffect(pagerState.currentPage) {
|
||||
scale = 1f
|
||||
offset = androidx.compose.ui.geometry.Offset.Zero
|
||||
canScroll = true
|
||||
}
|
||||
|
||||
AsyncImage(
|
||||
model = media.url,
|
||||
imageLoader = core.utils.CoilUtils.getGifImageLoader(LocalContext.current),
|
||||
contentDescription = null,
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.graphicsLayer(
|
||||
scaleX = scale,
|
||||
scaleY = scale,
|
||||
translationX = offset.x,
|
||||
translationY = offset.y
|
||||
),
|
||||
contentScale = ContentScale.Fit
|
||||
)
|
||||
} else {
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures(
|
||||
onDoubleTap = {
|
||||
if (scale > 1f) {
|
||||
scale = 1f
|
||||
offset = Offset.Zero
|
||||
canScroll = true
|
||||
} else {
|
||||
scale = 3f
|
||||
canScroll = false
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
.pointerInput(Unit) {
|
||||
awaitEachGesture {
|
||||
do {
|
||||
val event = awaitPointerEvent()
|
||||
val zoomActive = scale > 1f || event.changes.size > 1
|
||||
|
||||
if (zoomActive) {
|
||||
// Если мы в режиме зума - поглощаем события
|
||||
event.changes.forEach { it.consume() }
|
||||
|
||||
// Сами вычисляем трансформацию
|
||||
val zoomChange = event.calculateZoom()
|
||||
val panChange = event.calculatePan()
|
||||
|
||||
scale = (scale * zoomChange).coerceIn(1f, 5f)
|
||||
canScroll = scale <= 1f
|
||||
|
||||
if (scale > 1f) {
|
||||
offset += panChange
|
||||
} else {
|
||||
offset = Offset.Zero
|
||||
}
|
||||
}
|
||||
// Если zoomActive == false, мы НЕ вызываем consume(),
|
||||
// и событие уходит наверх в HorizontalPager
|
||||
} while (event.changes.any { it.pressed })
|
||||
}
|
||||
},
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
AsyncImage(
|
||||
model = media.url,
|
||||
imageLoader = core.utils.CoilUtils.getGifImageLoader(LocalContext.current),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.graphicsLayer(
|
||||
scaleX = scale,
|
||||
scaleY = scale,
|
||||
translationX = offset.x,
|
||||
translationY = offset.y
|
||||
),
|
||||
contentScale = ContentScale.Fit
|
||||
)
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Document / File placeholder in Lightbox
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize().padding(32.dp),
|
||||
|
||||
Reference in New Issue
Block a user