From 778052bd83171700a548bb9bf501c77c3be14eec Mon Sep 17 00:00:00 2001 From: SharmaPrateek196 Date: Wed, 16 Mar 2022 12:07:16 +0530 Subject: [PATCH] impl: SpringWaveAnimation --- .../baseio/composeplayground/MainActivity.kt | 6 +- .../contributors/PrateekSharma.kt | 31 ++++ .../ui/animations/springwave/Oval3D.kt | 63 +++++++ .../springwave/SpringWaveAnimation.kt | 161 ++++++++++++++++++ app/src/main/res/values/strings.xml | 2 + 5 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/dev/baseio/composeplayground/contributors/PrateekSharma.kt create mode 100644 app/src/main/java/dev/baseio/composeplayground/ui/animations/springwave/Oval3D.kt create mode 100644 app/src/main/java/dev/baseio/composeplayground/ui/animations/springwave/SpringWaveAnimation.kt diff --git a/app/src/main/java/dev/baseio/composeplayground/MainActivity.kt b/app/src/main/java/dev/baseio/composeplayground/MainActivity.kt index 1b20171..e318d65 100644 --- a/app/src/main/java/dev/baseio/composeplayground/MainActivity.kt +++ b/app/src/main/java/dev/baseio/composeplayground/MainActivity.kt @@ -23,6 +23,7 @@ import com.google.accompanist.pager.rememberPagerState import dev.baseio.composeplayground.ui.animations.* import dev.baseio.composeplayground.ui.animations.planetarysystem.PlanetarySystem import dev.baseio.composeplayground.ui.animations.pulltorefresh.PullToRefreshOne +import dev.baseio.composeplayground.ui.animations.springwave.SpringWaveAnimation import dev.baseio.composeplayground.ui.theme.ComposePlaygroundTheme class MainActivity : ComponentActivity() { @@ -54,7 +55,7 @@ class MainActivity : ComponentActivity() { modifier = Modifier .weight(1f) .fillMaxWidth(), - count = 10, state = pagerState, + count = 11, state = pagerState, ) { page -> // Our page content when (page) { @@ -106,6 +107,9 @@ class MainActivity : ComponentActivity() { BellAnimation(Modifier.align(Alignment.Center)) } } + 10 -> { + SpringWaveAnimation() + } } } HorizontalPagerIndicator( diff --git a/app/src/main/java/dev/baseio/composeplayground/contributors/PrateekSharma.kt b/app/src/main/java/dev/baseio/composeplayground/contributors/PrateekSharma.kt new file mode 100644 index 0000000..467569a --- /dev/null +++ b/app/src/main/java/dev/baseio/composeplayground/contributors/PrateekSharma.kt @@ -0,0 +1,31 @@ +package dev.baseio.composeplayground.contributors + +import androidx.compose.foundation.layout.* +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import dev.baseio.composeplayground.R +import dev.baseio.composeplayground.ui.theme.Typography + +const val prateekImageUrl = "https://pbs.twimg.com/profile_images/1496554615688425472/M6rm_jwG_normal.jpg" + +@Composable +fun PrateekSharma(modifier: Modifier = Modifier) { + Row(verticalAlignment = Alignment.CenterVertically, modifier = modifier.padding(4.dp)) { + CoilImageBox(Modifier.size(64.dp), prateekImageUrl) + Column(verticalArrangement = Arrangement.Center, modifier = Modifier.padding(8.dp)) { + Text( + text = stringResource(id = R.string.prateek), + style = Typography.h6.copy(MaterialTheme.colors.onSurface), + ) + Text( + text = stringResource(id = R.string.prateek_email), + style = Typography.subtitle1.copy(MaterialTheme.colors.onSurface), + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/dev/baseio/composeplayground/ui/animations/springwave/Oval3D.kt b/app/src/main/java/dev/baseio/composeplayground/ui/animations/springwave/Oval3D.kt new file mode 100644 index 0000000..7b88651 --- /dev/null +++ b/app/src/main/java/dev/baseio/composeplayground/ui/animations/springwave/Oval3D.kt @@ -0,0 +1,63 @@ +package dev.baseio.composeplayground.ui.animations.springwave + +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.material.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Rect +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.* +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp + + +/* +* Give [ Width = 3 * Height ] to see better view of the oval in 3D +* */ +@Composable +fun Oval3D( + givenSurfaceWidth: Float, + givenSurfaceHeight: Float, + modifier: Modifier +) { + Surface(modifier = modifier + .width(givenSurfaceWidth.dp) + .height(givenSurfaceHeight.dp), + color = Color.White, + shape = object : Shape { + override fun createOutline( + size: Size, + layoutDirection: LayoutDirection, + density: Density + ): Outline { + val thickness = 14f + val p1 = Path().apply { + addOval(Rect(0f, 0f, size.width, size.height)) + } + val p2 = Path().apply { + addOval( + Rect( + thickness + 5, + thickness - 9, + size.width - thickness - 5, + size.height - thickness - 3 + ) + ) + } + val p3 = Path() + p3.op(p1, p2, PathOperation.Difference) + return Outline.Generic(p3) + } + } + ) { + } +} + +@Composable +@Preview +fun Oval3DPrev() { + Oval3D(givenSurfaceWidth = 99f, givenSurfaceHeight = 33f, Modifier) +} \ No newline at end of file diff --git a/app/src/main/java/dev/baseio/composeplayground/ui/animations/springwave/SpringWaveAnimation.kt b/app/src/main/java/dev/baseio/composeplayground/ui/animations/springwave/SpringWaveAnimation.kt new file mode 100644 index 0000000..202a6e9 --- /dev/null +++ b/app/src/main/java/dev/baseio/composeplayground/ui/animations/springwave/SpringWaveAnimation.kt @@ -0,0 +1,161 @@ +package dev.baseio.composeplayground.ui.animations.springwave + +import androidx.compose.animation.core.* +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.material.Button +import androidx.compose.material.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.* +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import dev.baseio.composeplayground.contributors.PrateekSharma +import dev.baseio.composeplayground.ui.animations.springwave.AnimationState.End +import dev.baseio.composeplayground.ui.animations.springwave.AnimationState.Start +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +@Composable +fun SpringWaveAnimation() { + val smallestWidth = 27f + val smallestHeight = 9f + + var currentAnimationState by remember { mutableStateOf(Start)} + + val circle1offsetState = remember { + Animatable(if(currentAnimationState==Start) 500f else 200f) + } + + val circle2offsetState = remember { + Animatable(if(currentAnimationState==Start) 497.5f else 197.5f) + } + + val circle3offsetState = remember { + Animatable(if(currentAnimationState==Start) 495f else 195f) + } + + val circle4offsetState = remember { + Animatable(if(currentAnimationState==Start) 492.5f else 192.5f) + } + + val circle5offsetState = remember { + Animatable(if(currentAnimationState==Start) 489.75f else 189.75f) + } + + val circle6offsetState = remember { + Animatable(if(currentAnimationState==Start) 487f else 187f) + } + + val circle7offsetState = remember { + Animatable(if(currentAnimationState==Start) 484f else 184f) + } + + LaunchedEffect(key1 = currentAnimationState) { + launch { + /* + * Need to launch these coroutines in different different + * scopes because we want that they all run simultaneously + * with there own delays from the global starting time. + * + * If we would have used these all animateTo() methods in + * a single coroutine scope then they would run sequentially + * one after another; means that the second ring would start moving + * only after the damping effect of the first ring completes. + * */ + + launch { + circle1offsetState.animateTo( + if(currentAnimationState==Start) 200f else 500f, + spring(Spring.DampingRatioHighBouncy, Spring.StiffnessVeryLow)) + } + launch { + delay(40) + circle2offsetState.animateTo( + if(currentAnimationState==Start) 197.5f else 497.5f, + spring(Spring.DampingRatioHighBouncy, Spring.StiffnessVeryLow)) + } + launch { + delay(90) + circle3offsetState.animateTo( + if(currentAnimationState==Start) 195f else 495f, + spring(Spring.DampingRatioHighBouncy, Spring.StiffnessVeryLow)) + } + launch { + delay(130) + circle4offsetState.animateTo( + if(currentAnimationState==Start) 192.5f else 492.5f, + spring(Spring.DampingRatioHighBouncy, Spring.StiffnessVeryLow)) + } + launch { + delay(170) + circle5offsetState.animateTo( + if(currentAnimationState==Start) 189.75f else 489.75f, + spring(Spring.DampingRatioHighBouncy, Spring.StiffnessVeryLow)) + } + launch { + delay(210) + circle6offsetState.animateTo( + if(currentAnimationState==Start) 187f else 487f, + spring(Spring.DampingRatioHighBouncy, Spring.StiffnessVeryLow)) + } + launch { + delay(250) + circle7offsetState.animateTo( + if(currentAnimationState==Start) 184f else 484f, + spring(Spring.DampingRatioHighBouncy, Spring.StiffnessVeryLow) + ) + } + } + } + + BoxWithConstraints( + modifier = Modifier + .background(Color.Black) + .fillMaxSize() + ) { + Oval3D(givenSurfaceWidth = smallestWidth, givenSurfaceHeight = smallestHeight, modifier = Modifier.absoluteOffset(this.maxWidth/2, circle1offsetState.value.dp)) + Oval3D(givenSurfaceWidth = smallestWidth*2, givenSurfaceHeight = smallestHeight*2.1f, modifier = Modifier.absoluteOffset(this.maxWidth/2-(13).dp, circle2offsetState.value.dp)) + Oval3D(givenSurfaceWidth = smallestWidth*3, givenSurfaceHeight = smallestHeight*3.2f, modifier = Modifier.absoluteOffset(this.maxWidth/2-(26.5).dp, circle3offsetState.value.dp)) + Oval3D(givenSurfaceWidth = smallestWidth*4, givenSurfaceHeight = smallestHeight*4.4f, modifier = Modifier.absoluteOffset(this.maxWidth/2-40.dp, circle4offsetState.value.dp)) + Oval3D(givenSurfaceWidth = smallestWidth*5, givenSurfaceHeight = smallestHeight*5.6f, modifier = Modifier.absoluteOffset(this.maxWidth/2-53.dp, circle5offsetState.value.dp)) + Oval3D(givenSurfaceWidth = smallestWidth*6, givenSurfaceHeight = smallestHeight*6.9f, modifier = Modifier.absoluteOffset(this.maxWidth/2-(67).dp, circle6offsetState.value.dp)) + Oval3D(givenSurfaceWidth = smallestWidth*7, givenSurfaceHeight = smallestHeight*8.2f, modifier = Modifier.absoluteOffset(this.maxWidth/2-(80).dp, circle7offsetState.value.dp)) + + PrateekSharma( + Modifier.background(Color.White).absoluteOffset(y=5.dp) + ) + + Button( + modifier = Modifier + .align(Alignment.BottomCenter) + .wrapContentSize(align = Alignment.Center), + onClick = { + if(currentAnimationState == End) { + currentAnimationState = Start + } else { + currentAnimationState = End + } + }) { + Text( + text = if(currentAnimationState==Start) { + "Move the wave to down" + } else { + "Move the wave to Up" + } + ) + } + } +} + +enum class AnimationState { + Start, End +} + + +@Preview(showBackground = true, showSystemUi = true) +@Composable +fun SpringWaveAnimPrev() { + SpringWaveAnimation() +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a144b92..c61403d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2,6 +2,8 @@ Compose Playground Anmol Verma anmol.verma@mutualmobile.com\nanmol.verma4@gmail.com + Prateek Sharma + sharmaprateek196@gmail.com LOADING \ No newline at end of file