@@ -4,7 +4,9 @@ import androidx.compose.animation.core.Animatable
44import androidx.compose.animation.core.Spring
55import androidx.compose.animation.core.spring
66import androidx.compose.foundation.Image
7+ import androidx.compose.foundation.clickable
78import androidx.compose.foundation.gestures.detectVerticalDragGestures
9+ import androidx.compose.foundation.interaction.MutableInteractionSource
810import androidx.compose.foundation.layout.Arrangement
911import androidx.compose.foundation.layout.Box
1012import androidx.compose.foundation.layout.Row
@@ -17,6 +19,9 @@ import androidx.compose.foundation.layout.systemBarsPadding
1719import androidx.compose.foundation.lazy.LazyColumn
1820import androidx.compose.foundation.lazy.items
1921import androidx.compose.runtime.Composable
22+ import androidx.compose.runtime.LaunchedEffect
23+ import androidx.compose.runtime.getValue
24+ import androidx.compose.runtime.mutableFloatStateOf
2025import androidx.compose.runtime.mutableStateOf
2126import androidx.compose.runtime.remember
2227import androidx.compose.runtime.rememberCoroutineScope
@@ -28,6 +33,11 @@ import androidx.compose.ui.input.pointer.pointerInput
2833import androidx.compose.ui.res.painterResource
2934import androidx.compose.ui.tooling.preview.Preview
3035import androidx.compose.ui.unit.dp
36+ import com.airbnb.lottie.compose.LottieAnimation
37+ import com.airbnb.lottie.compose.LottieCompositionSpec
38+ import com.airbnb.lottie.compose.LottieConstants
39+ import com.airbnb.lottie.compose.rememberLottieAnimatable
40+ import com.airbnb.lottie.compose.rememberLottieComposition
3141import com.paradoxo.threadscompose.R
3242import com.paradoxo.threadscompose.model.Post
3343import kotlinx.coroutines.launch
@@ -45,7 +55,7 @@ fun FeedScreen(
4555 .systemBarsPadding()
4656 ) {
4757 item {
48- ExpandableAppLogo ()
58+ ExpandableAppLogoLottie ()
4959 }
5060
5161 items(
@@ -70,6 +80,107 @@ fun FeedScreen(
7080 }
7181}
7282
83+ @Composable
84+ private fun ExpandableAppLogoLottie () {
85+ val maxHeightImage = 200 .dp
86+ val defaultSizeImage = 72 .dp
87+ val coroutineScope = rememberCoroutineScope()
88+ val animatedImageSize = remember { Animatable (defaultSizeImage.value) }
89+
90+
91+ val lottiePreviousProgress by remember { mutableFloatStateOf(0f ) }
92+ val lottieSpeed by remember { mutableFloatStateOf(1f ) }
93+ val lottieComposition by rememberLottieComposition(
94+ LottieCompositionSpec .RawRes (
95+ R .raw.logo_lines_animated
96+ )
97+ )
98+ val lottieAnimatable = rememberLottieAnimatable()
99+
100+ LaunchedEffect (lottieSpeed) {
101+ lottieAnimatable.animate(
102+ lottieComposition,
103+ iteration = LottieConstants .IterateForever ,
104+ speed = lottieSpeed,
105+ )
106+ }
107+
108+ Box (
109+ modifier = Modifier
110+ .pointerInput(Unit ) {
111+ detectVerticalDragGestures(
112+ onVerticalDrag = { change, offset ->
113+ coroutineScope.launch {
114+ val newSize =
115+ (animatedImageSize.value + offset / 8 ).coerceAtLeast(
116+ defaultSizeImage.value
117+ )
118+ if (newSize < maxHeightImage.value) {
119+ animatedImageSize.snapTo(newSize)
120+ }
121+ change.consume()
122+ }
123+ },
124+ onDragEnd = {
125+ coroutineScope.launch {
126+ animatedImageSize.animateTo(
127+ defaultSizeImage.value,
128+ animationSpec = spring(
129+ dampingRatio = Spring .DampingRatioLowBouncy ,
130+ stiffness = Spring .StiffnessLow
131+ )
132+ )
133+ }
134+ coroutineScope.launch {
135+ lottieAnimatable.animate(
136+ lottieComposition,
137+ iteration = 1 ,
138+ speed = lottieSpeed,
139+ reverseOnRepeat = true ,
140+ initialProgress = lottiePreviousProgress,
141+ )
142+ }
143+ },
144+ onDragStart = {
145+ coroutineScope.launch {
146+ lottieAnimatable.animate(
147+ lottieComposition,
148+ iteration = LottieConstants .IterateForever ,
149+ speed = lottieSpeed,
150+ initialProgress = lottiePreviousProgress,
151+ )
152+ }
153+ }
154+ )
155+ }
156+ ) {
157+ // To remove click animation of clickable modifier
158+ val interactionSource = remember { MutableInteractionSource () }
159+
160+ Row (
161+ modifier = Modifier
162+ .fillMaxWidth()
163+ .clickable(
164+ interactionSource = interactionSource,
165+ indication = null
166+ ) {
167+ // lottieSpeed = if (lottieSpeed == 0f) 1f else 0f
168+ // lottiePreviousProgress = lottieAnimatable.progress
169+ },
170+ horizontalArrangement = Arrangement .Center ,
171+ ) {
172+
173+ LottieAnimation (
174+ composition = lottieComposition,
175+ progress = { lottieAnimatable.progress },
176+ modifier = Modifier
177+ .size(animatedImageSize.value.dp)
178+ )
179+
180+ }
181+ }
182+ }
183+
73184@Composable
74185private fun ExpandableAppLogo () {
75186 val maxHeightImage = 200 .dp
0 commit comments