Skip to content

Commit

Permalink
Merge pull request #35 from prasidhanchan/v1.x
Browse files Browse the repository at this point in the history
feat: add pinch to zoom and pan functionality to posts
  • Loading branch information
prasidhanchan authored Oct 4, 2024
2 parents efc9fcc + 6655e74 commit 01ea329
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 19 deletions.
45 changes: 41 additions & 4 deletions core/ui/src/main/java/com/mca/ui/component/CMPager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ package com.mca.ui.component

import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.rememberTransformableState
import androidx.compose.foundation.gestures.transformable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
Expand All @@ -23,6 +25,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
Expand All @@ -34,24 +37,34 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.scale
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import com.mca.ui.R
import com.mca.ui.theme.Black
import com.mca.ui.theme.LightBlack
import com.mca.ui.theme.tintColor
import com.mca.util.constant.animateAlpha

/**
* Horizontal pager composable to display post images along with the dots indicator.
* Includes additional features such as pinch to zoom and pan.
* @param images List of image URLs to be displayed in the pager.
* @param state PagerState to control and observe the pager's state.
* @param modifier Modifier for styling and layout customization.
Expand All @@ -72,8 +85,24 @@ fun CMPager(
enableRemoveIcon: Boolean = false,
enableClick: Boolean = false,
onClick: () -> Unit = { },
onRemoveImageClick: (image: String) -> Unit = { }
onRemoveImageClick: (image: String) -> Unit = { },
onTransform: (Boolean) -> Unit = { }
) {
var scale by remember { mutableFloatStateOf(1f) }
var offset by remember { mutableStateOf(Offset.Zero) }
val transformable = rememberTransformableState { zoomChange, panChange, _ ->
scale = (scale * zoomChange).coerceAtLeast(1f)
if (scale != 1f) offset = (offset + panChange)
}

LaunchedEffect(key1 = transformable.isTransformInProgress) {
if (!transformable.isTransformInProgress) {
scale = 1f
offset = Offset.Zero
}
if (transformable.isTransformInProgress) onTransform(true) else onTransform(false)
}

Column(
modifier = Modifier
.fillMaxWidth()
Expand All @@ -83,15 +112,18 @@ fun CMPager(
) {
Surface(
modifier = modifier
.padding(top = 4.dp, bottom = 10.dp)
.padding(bottom = 10.dp)
.fillMaxWidth()
.height(220.dp)
.clickable(
enabled = enableClick,
indication = null,
interactionSource = remember(::MutableInteractionSource),
onClick = onClick
),
)
.transformable(transformable)
.scale(scale)
.offset { IntOffset(offset.x.toInt(), offset.y.toInt()) },
shape = RoundedCornerShape(10.dp),
color = LightBlack
) {
Expand Down Expand Up @@ -138,7 +170,12 @@ fun CMPager(

PagerDot(
pageCount = state.pageCount,
selectedPage = state.currentPage
selectedPage = state.currentPage,
modifier = Modifier.animateAlpha(
delay = 0,
duration = 250,
condition = !transformable.isTransformInProgress
)
)
}
}
Expand Down
19 changes: 15 additions & 4 deletions core/util/src/main/java/com/mca/util/constant/UtilAnimations.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,30 @@ fun Modifier.animatedLike(onClick: () -> Unit) = composed {
/**
* Animate the alpha of any composable function such as a Card.
* @param delay The delay of the enter animation in milliseconds.
* @param duration The duration of the enter animation in milliseconds.
* @param condition The condition when the composable should be shown.
*/
fun Modifier.animateAlpha(delay: Int) = composed {
fun Modifier.animateAlpha(
delay: Int,
duration: Int = 800,
condition: Boolean = true
) = composed {
var alpha by rememberSaveable { mutableFloatStateOf(0f) }
val animatedAlpha by animateFloatAsState(
targetValue = alpha,
animationSpec = tween(
durationMillis = 800,
durationMillis = duration,
delayMillis = delay
),
label = "animatedNotificationAlpha"
)
alpha(animatedAlpha)
.onGloballyPositioned { alpha = 1f }
if (condition) {
alpha(animatedAlpha)
.onGloballyPositioned { alpha = 1f }
} else {
alpha(animatedAlpha)
.onGloballyPositioned { alpha = 0f }
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
Expand All @@ -44,6 +45,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
Expand All @@ -63,6 +65,7 @@ import com.mca.ui.theme.dosis
import com.mca.ui.theme.fontColor
import com.mca.ui.theme.tintColor
import com.mca.util.constant.Constant.ADMIN
import com.mca.util.constant.animateAlpha
import com.mca.util.constant.animatedLike
import com.mca.util.constant.toLikedBy
import com.mca.util.constant.toLikes
Expand All @@ -83,6 +86,8 @@ internal fun AnnouncementCard(
onLikeClick: (postId: String, token: String) -> Unit,
onUnlikeCLick: (postId: String) -> Unit
) {
var alpha by remember { mutableFloatStateOf(1f) }

Column(
modifier = modifier
.animateContentSize(animationSpec = tween(durationMillis = 400))
Expand All @@ -108,18 +113,29 @@ internal fun AnnouncementCard(
) {
Column(
modifier = modifier
.padding(all = 10.dp)
.padding(all = 15.dp)
.fillMaxWidth()
.wrapContentHeight(Alignment.CenterVertically),
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.Start
) {
MainContent(post = post)
MainContent(
post = post,
alpha = alpha,
onTransform = { transforming ->
alpha = if (transforming) 0f else 1f
}
)
LikesAndTimeStamp(
post = post,
token = user.token,
currentUserId = currentUserId,
currentUsername = currentUsername,
modifier = Modifier.animateAlpha(
delay = 0,
duration = 250,
condition = alpha == 1f
),
onLikeCLick = onLikeClick,
onUnlikeCLick = onUnlikeCLick
)
Expand Down Expand Up @@ -194,10 +210,13 @@ private fun AnnouncementTopBar(
}

@Composable
private fun AnnouncementBottomBar(likes: List<String>) {
private fun AnnouncementBottomBar(
likes: List<String>,
modifier: Modifier = Modifier
) {
Spacer(modifier = Modifier.height(20.dp))
Row(
modifier = Modifier.fillMaxWidth(),
modifier = modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
) {
Expand All @@ -224,7 +243,9 @@ private fun AnnouncementBottomBar(likes: List<String>) {
@Composable
private fun MainContent(
post: Post,
modifier: Modifier = Modifier
modifier: Modifier = Modifier,
alpha: Float,
onTransform: (Boolean) -> Unit
) {
val state = rememberPagerState { post.images.size }
var isOpen by remember { mutableStateOf(false) }
Expand All @@ -233,7 +254,9 @@ private fun MainContent(
CMPager(
images = post.images,
state = state,
modifier = modifier
modifier = modifier,
contentScale = ContentScale.Crop,
onTransform = onTransform
)
}

Expand All @@ -247,11 +270,17 @@ private fun MainContent(
),
overflow = TextOverflow.Ellipsis,
maxLines = if (!isOpen) 5 else Int.MAX_VALUE,
modifier = Modifier.clickable(
indication = null,
interactionSource = remember(::MutableInteractionSource),
onClick = { isOpen = !isOpen }
)
modifier = Modifier
.clickable(
indication = null,
interactionSource = remember(::MutableInteractionSource),
onClick = { isOpen = !isOpen }
)
.animateAlpha(
delay = 0,
duration = 250,
condition = alpha == 1f
)
)
}

Expand Down

0 comments on commit 01ea329

Please sign in to comment.