1+ package com.smarttoolfactory.tutorial1_1basics.chapter6_graphics
2+
3+ import android.content.res.Resources
4+ import androidx.compose.foundation.Canvas
5+ import androidx.compose.foundation.border
6+ import androidx.compose.foundation.layout.Arrangement
7+ import androidx.compose.foundation.layout.BoxWithConstraints
8+ import androidx.compose.foundation.layout.Column
9+ import androidx.compose.foundation.layout.Spacer
10+ import androidx.compose.foundation.layout.fillMaxSize
11+ import androidx.compose.foundation.layout.fillMaxWidth
12+ import androidx.compose.foundation.layout.height
13+ import androidx.compose.foundation.layout.padding
14+ import androidx.compose.foundation.layout.size
15+ import androidx.compose.material.Slider
16+ import androidx.compose.material.Text
17+ import androidx.compose.runtime.Composable
18+ import androidx.compose.runtime.getValue
19+ import androidx.compose.runtime.mutableFloatStateOf
20+ import androidx.compose.runtime.remember
21+ import androidx.compose.runtime.setValue
22+ import androidx.compose.ui.Alignment
23+ import androidx.compose.ui.Modifier
24+ import androidx.compose.ui.geometry.Offset
25+ import androidx.compose.ui.graphics.Color
26+ import androidx.compose.ui.platform.LocalDensity
27+ import androidx.compose.ui.tooling.preview.Preview
28+ import androidx.compose.ui.unit.Dp
29+ import androidx.compose.ui.unit.dp
30+ import androidx.compose.ui.unit.sp
31+ import java.util.Random
32+
33+ @Preview
34+ @Composable
35+ fun ControlledExplosion () {
36+ Column (
37+ modifier = Modifier .fillMaxSize().padding(16 .dp),
38+ horizontalAlignment = Alignment .CenterHorizontally
39+ ) {
40+ var progress by remember { mutableFloatStateOf(0f ) }
41+
42+ Explosion (progress)
43+
44+ Spacer (Modifier .height(16 .dp))
45+ Text (text = " Progress: $progress " , fontSize = 18 .sp)
46+ Slider (
47+ modifier = Modifier .fillMaxWidth(),
48+ value = progress,
49+ onValueChange = {
50+ progress = it
51+ }
52+ )
53+ Spacer (Modifier .height(16 .dp))
54+ }
55+ }
56+
57+ @Composable
58+ fun Explosion (
59+ progress : Float
60+ ) {
61+ BoxWithConstraints {
62+
63+ val sizeDp = maxWidth
64+ val density = LocalDensity .current
65+ val sizePx = with (density) {
66+ sizeDp.toPx()
67+ }
68+ val sizePxHalf = sizePx / 2
69+ val particles = remember {
70+ List (150 ) {
71+ ExplodingParticle (
72+ color = Color (listOf (0xffea4335 , 0xff4285f4 , 0xfffbbc05 , 0xff34a853 ).random()),
73+ startXPosition = sizePxHalf.toInt(),
74+ startYPosition = sizePxHalf.toInt(),
75+ maxHorizontalDisplacement = sizePxHalf,
76+ maxVerticalDisplacement = sizePxHalf
77+ )
78+ }
79+ }
80+ particles.forEach { it.updateProgress(progress) }
81+
82+ Canvas (
83+ modifier = Modifier
84+ .border(width = 1 .dp, color = Color (0x26000000 ))
85+ .size(sizeDp)
86+ ) {
87+ drawLine(
88+ color = Color .Black ,
89+ start = Offset (sizePxHalf, 0f ),
90+ end = Offset (sizePxHalf, sizePx),
91+ strokeWidth = 2 .dp.toPx()
92+ )
93+ drawLine(
94+ color = Color .Black ,
95+ start = Offset (0f , sizePxHalf),
96+ end = Offset (sizePx, sizePxHalf),
97+ strokeWidth = 2 .dp.toPx()
98+ )
99+ particles.forEach { particle ->
100+ drawCircle(
101+ alpha = particle.alpha,
102+ color = particle.color,
103+ radius = 5 .dp.toPx(),
104+ center = Offset (particle.currentXPosition, particle.currentYPosition),
105+ )
106+ }
107+ }
108+ }
109+ }
110+
111+ class ExplodingParticle (
112+ val color : Color ,
113+ val startXPosition : Int ,
114+ val startYPosition : Int ,
115+ val maxHorizontalDisplacement : Float ,
116+ val maxVerticalDisplacement : Float
117+ ) {
118+ private val velocity = 4 * maxVerticalDisplacement
119+ private val acceleration = - 2 * velocity
120+ var currentXPosition = 0f
121+ var currentYPosition = 0f
122+
123+ var alpha = 1f
124+
125+ fun updateProgress (explosionProgress : Float ) {
126+ val currentTime = explosionProgress * 1f
127+
128+ val verticalDisplacement =
129+ currentTime * velocity + 0.5 * acceleration * currentTime * currentTime
130+
131+ currentXPosition = startXPosition + maxHorizontalDisplacement * explosionProgress
132+ currentYPosition = (startYPosition - verticalDisplacement).toFloat()
133+
134+ }
135+ }
136+
137+ fun Float.mapInRange (inMin : Float , inMax : Float , outMin : Float , outMax : Float ): Float {
138+ return outMin + (((this - inMin) / (inMax - inMin)) * (outMax - outMin))
139+ }
140+
141+ fun Int.dpToPx () = toFloat().dpToPx()
142+ fun Dp.toPx () = value.dpToPx()
143+
144+ fun Float.dpToPx () = this * Resources .getSystem().displayMetrics.density
145+
146+
147+ private val random = Random ()
148+ fun Float.randomTillZero () = this * random.nextFloat()
149+ fun randomInRange (min : Float , max : Float ) = min + (max - min).randomTillZero()
150+ fun randomBoolean (trueProbabilityPercentage : Int ) =
151+ random.nextFloat() < trueProbabilityPercentage / 100f
0 commit comments