Last active
April 8, 2024 00:35
-
-
Save ChathuraHettiarachchi/9c7dfee21ee855162c7e20fd1441839f to your computer and use it in GitHub Desktop.
I found an awesome SwiftUI implementation on SpringRings Ui which made me try out it with Jetpack compose Cavas API. Find the video on https://www.linkedin.com/posts/chathurajh_jetpackcompose-canvasapi-activity-7182898304748326914-liw4?utm_source=share&utm_medium=member_desktop
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.choota.dev.composetryout | |
import android.os.Bundle | |
import androidx.activity.ComponentActivity | |
import androidx.activity.compose.setContent | |
import androidx.compose.animation.core.Animatable | |
import androidx.compose.animation.core.Spring | |
import androidx.compose.animation.core.spring | |
import androidx.compose.foundation.Canvas | |
import androidx.compose.foundation.background | |
import androidx.compose.foundation.layout.Box | |
import androidx.compose.foundation.layout.fillMaxHeight | |
import androidx.compose.foundation.layout.fillMaxSize | |
import androidx.compose.foundation.layout.offset | |
import androidx.compose.foundation.layout.size | |
import androidx.compose.material3.MaterialTheme | |
import androidx.compose.material3.Surface | |
import androidx.compose.material3.Text | |
import androidx.compose.runtime.Composable | |
import androidx.compose.runtime.LaunchedEffect | |
import androidx.compose.runtime.getValue | |
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.graphics.Color | |
import androidx.compose.ui.graphics.drawscope.DrawScope | |
import androidx.compose.ui.graphics.drawscope.Stroke | |
import androidx.compose.ui.graphics.graphicsLayer | |
import androidx.compose.ui.tooling.preview.Preview | |
import androidx.compose.ui.unit.Dp | |
import androidx.compose.ui.unit.dp | |
import com.xero.interview.composetryout.ui.theme.ComposeTryOutTheme | |
import kotlinx.coroutines.delay | |
import kotlinx.coroutines.flow.Flow | |
import kotlinx.coroutines.flow.flow | |
class MainActivity : ComponentActivity() { | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
setContent { | |
ComposeTryOutTheme { | |
Surface( | |
modifier = Modifier.fillMaxSize(), | |
color = MaterialTheme.colorScheme.background | |
) { | |
SpringRings() | |
} | |
} | |
} | |
} | |
} | |
@Preview | |
@Composable | |
fun SpringRings() { | |
var moved by remember { | |
mutableStateOf(false) | |
} | |
// to update move status periodically, we can have all in the effect block too | |
// but this looks fancy :) | |
LaunchedEffect(true) { | |
infiniteLoopFlow.collect { | |
moved = it % 2 == 0 | |
} | |
} | |
Box( | |
modifier = Modifier.background(color = Color.Black).fillMaxSize(), | |
contentAlignment = Alignment.Center | |
) { | |
for (i in 0 until 12) { | |
Ring(id = i, moved = moved, size = (16 + 22 * i).dp) | |
} | |
} | |
} | |
@Composable | |
fun Ring( | |
id: Int = 0, | |
moved: Boolean = false, | |
size: Dp, | |
strokeWidth: Float = 16f | |
) { | |
// offset needs to animate make it go up and down | |
val offset = remember { | |
Animatable(-150f) | |
} | |
LaunchedEffect(moved) { | |
delay((80 + 40 * id).toLong()) // dynamic delay based on id, having const 80 and adding dynamic multiplier making it more space with more delay | |
offset.animateTo( | |
targetValue = if(moved) 150f else -150f, | |
animationSpec = spring( | |
dampingRatio = Spring.DampingRatioHighBouncy - 0.002f * id, // having id here can have dynamic dampingRatio | |
stiffness = Spring.StiffnessVeryLow + 15f * id, // having id here can have dynamic stiffness | |
) | |
) | |
} | |
Canvas(modifier = Modifier | |
.size(size) | |
.offset(y = (offset.value).dp) | |
.graphicsLayer { rotationX = 70f } // to rotate the x axis, by doing this we can see the circles taking oval shape, but they are actually having circle shape | |
) { | |
drawRing(strokeWidth) | |
} | |
} | |
// extension to draw an arc (this this it's circle from 0-360 degrees) | |
private fun DrawScope.drawRing(strokeWidth: Float) { | |
drawArc( | |
color = Color.White, | |
startAngle = 0f, | |
sweepAngle = 360f, | |
useCenter = true, | |
style = Stroke(strokeWidth) | |
) | |
} | |
val infiniteLoopFlow: Flow<Int> = flow { | |
var id = 0 | |
while (true) { | |
emit(id) | |
id++ | |
delay(3500L) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment