Skip to content

impl: SpringWaveAnimation #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -106,6 +107,9 @@ class MainActivity : ComponentActivity() {
BellAnimation(Modifier.align(Alignment.Center))
}
}
10 -> {
SpringWaveAnimation()
}
}
}
HorizontalPagerIndicator(
Expand Down
Original file line number Diff line number Diff line change
@@ -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),
)
}
}
}
Original file line number Diff line number Diff line change
@@ -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)
}
Original file line number Diff line number Diff line change
@@ -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()
}
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<string name="app_name">Compose Playground</string>
<string name="emp_mmh0158">Anmol Verma</string>
<string name="emp_mmh0158_email">[email protected]\[email protected]</string>
<string name="prateek">Prateek Sharma</string>
<string name="prateek_email">[email protected]</string>
<string name="loading">LOADING</string>
<string name="sun_moon"><![CDATA[Sun & Moon]]></string>
</resources>