Skip to content

Instantly share code, notes, and snippets.

@nirbhayph
Last active April 14, 2024 19:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nirbhayph/2be482a269a4b7098dfba78ef8d597a4 to your computer and use it in GitHub Desktop.
Save nirbhayph/2be482a269a4b7098dfba78ef8d597a4 to your computer and use it in GitHub Desktop.
Delving into Canvas for Custom Graphics and Animations with Jetpack Compose
@Composable
fun Chart(){
val temperatureData = listOf(
DataPoint(20f, SampleColors.lighterPink),
DataPoint(45f, SampleColors.lightBlue),
DataPoint(130f, SampleColors.darkPink),
DataPoint(80f, SampleColors.lightPink),
DataPoint(65f, Color.Cyan)
)
BarChart(data = temperatureData)
}
data class DataPoint(val value: Float, val color: Color)
@Composable
fun BarChart(data: List<TemperaturePoint>) {
val maxBarValue = data.maxOf { it.value }
Canvas(
modifier = Modifier.fillMaxSize()
.background(Brush
.horizontalGradient(
listOf(
Color.White,
Color.Red,
Color.Yellow))
.padding(20.dp)
) {
val maxBarHeight = size.height
val barWidth = size.width / data.size
data.forEachIndexed { index, dataPoint ->
val barHeight = (dataPoint.value / maxBarValue) * maxBarHeight
drawRect(
color = dataPoint.color,
topLeft = Offset(index * barWidth, size.height - barHeight),
size = Size(barWidth, barHeight)
)
}
}
}
@Composable
fun BouncingBallGame() {
val position = remember { mutableStateOf(Offset(300f, 0f)) }
val velocity = remember { mutableStateOf(Offset(3f, 3f)) }
LaunchedEffect(Unit) {
while (true) {
position.value += velocity.value
// change > condition to size of screen,
// here just using a static value for demo purposes
if (position.value.x < 0f || position.value.x > 1000f) {
velocity.value = Offset(-velocity.value.x, velocity.value.y)
}
if (position.value.y < 0f || position.value.y > 1200f) {
velocity.value = Offset(velocity.value.x, -velocity.value.y)
}
delay(16L)
}
}
Column(
modifier = Modifier.background(
brush = Brush.horizontalGradient(
listOf(
Color.Red,
Color.Blue
)
)
)
) {
Canvas(
modifier = Modifier
.height(460.dp)
.fillMaxWidth()
) {
drawCircle(
brush = Brush.horizontalGradient(listOf(Color.Blue, Color.Red)),
center = position.value,
radius = 60f
)
}
Divider(thickness = 5.dp, color = Color.Red)
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Button(
colors = ButtonDefaults.buttonColors(containerColor = Color.Red),
onClick = {
val random = (-1..1).random()
if (random != 0) {
velocity.value += Offset(random * 10f, random * 10f)
}
},
) {
Text(text = "Change Bounce")
}
Text(
text = "Velocity: ${velocity.value.x}, ${velocity.value.y}",
color = Color.White,
)
}
}
}
@Composable
fun CanvasShapesAndPaths(){
Canvas(
modifier = Modifier
.fillMaxSize()
.background(SampleColors.lighterPink)
.padding(10.dp)) {
// Draw a rectangle
drawRect(color = SampleColors.lightPink)
// Draw a circle
drawCircle(color = SampleColors.darkPink, radius = 400f)
// Draw a custom path
val path = Path().apply {
moveTo(600f, 1200f)
lineTo(800f, 800f)
lineTo(350f, 400f)
lineTo(160f, 25f)
close()
}
drawPath(
path = path,
brush = Brush.horizontalGradient(
listOf(
SampleColors.lightBlue,
SampleColors.lighterPink
)
)
)
}
}
import androidx.compose.foundation.Canvas
@Composable
fun CustomCanvasExample() {
Canvas(
modifier = Modifier.fillMaxSize()
) {
// Your drawing code here
}
}
@Composable
fun MovingCircles() {
val position = remember { Animatable(0f) }
LaunchedEffect(Unit) {
position.animateTo(
targetValue = 550f,
animationSpec = tween(durationMillis = 3000)
)
}
Canvas(
modifier = Modifier.fillMaxSize().background(Color.Black)
) {
drawCircle(color = Color.Red, center = Offset(position.value, 500f), radius = 50f)
drawCircle(color = Color.Blue, center = Offset(position.value, 700f), radius = 80f)
drawCircle(color = Color.Magenta, center = Offset(position.value, 1000f), radius = 150f)
drawCircle(color = Color.Yellow, center = Offset(position.value, 1400f), radius = 190f)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val particlesData = listOf<Particle>(
Particle(Offset(100f, 1820f), Offset(1f, -2f)),
Particle(Offset(200f, 1680f), Offset(-0.5f, -1.5f)),
Particle(Offset(300f, 1700f), Offset(0.2f, -1.8f)),
Particle(Offset(400f, 1850f), Offset(-0.8f, -2f)),
Particle(Offset(500f, 1800f), Offset(0.4f, -1.2f)),
Particle(Offset(600f, 1960f), Offset(1f, -2f)),
Particle(Offset(700f, 1980f), Offset(-0.5f, -1.5f)),
Particle(Offset(800f, 2010f), Offset(0.2f, -1.8f)),
Particle(Offset(900f, 1950f), Offset(-0.8f, -2f)),
Particle(Offset(750f, 2000f), Offset(0.4f, -1.2f)),
Particle(Offset(100f, 1780f), Offset(1f, -2f)),
Particle(Offset(200f, 1790f), Offset(-0.5f, -1.5f)),
Particle(Offset(300f, 1805f), Offset(0.2f, -1.8f)),
Particle(Offset(400f, 1815f), Offset(-0.8f, -2f)),
Particle(Offset(500f, 1825f), Offset(0.4f, -1.2f)),
Particle(Offset(600f, 1915f), Offset(1f, -2f)),
Particle(Offset(700f, 1922f), Offset(-0.5f, -1.5f)),
Particle(Offset(800f, 2014f), Offset(0.2f, -1.8f)),
Particle(Offset(900f, 2012f), Offset(-0.8f, -2f)),
Particle(Offset(750f, 2004f), Offset(0.4f, -1.2f))
)
ParticleSystem(particlesData)
}
}
data class Particle(
val position: Offset,
val velocity: Offset
)
@Composable
fun ParticleSystem(particles: List<Particle>) {
val mutableParticles = remember { mutableStateListOf<Particle>() }
mutableParticles.addAll(particles)
var counter = 0
LaunchedEffect(Unit) {
while (true) {
val particlesCopy = ArrayList(mutableParticles.map { it.copy() })
particlesCopy.forEachIndexed { index, particle ->
mutableParticles[index] =
particle.copy(position = particle.position + particle.velocity)
}
delay(16L)
counter += 1
// modify as needed or change the condition to something like
// when all particles are out of bounds of the screen
if (counter > 3000) {
break
}
}
}
Canvas(
modifier = Modifier
.fillMaxSize()
.background(Brush.horizontalGradient(listOf(SampleColors.yellow, SampleColors.gold)))
) {
mutableParticles.forEach { particle ->
drawCircle(color = SampleColors.brown, alpha = 0.6F, center = particle.position, radius = 10f)
}
}
}
import androidx.compose.ui.graphics.Color
object SampleColors {
val lightPink = Color(0xFFB86Fff)
val lightBlue = Color(0xF194B4ff)
val darkPink = Color(0xFFB100ff)
val lighterPink = Color(0xFFEBC6ff)
val gold = Color(0xffFDC00D)
val yellow = Color(0xffFDF56E)
val brown = Color(0xffA17800)
}
@thproflord
Copy link

Hi, have you faced any problem with the bouncy ball when you have been a lot of time? because I have somethiing similiar and my screen freezes when i have been a lot of time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment