@@ -3,22 +3,22 @@ package com.smarttoolfactory.tutorial1_1basics.chapter6_graphics
3
3
import android.content.res.Resources
4
4
import androidx.compose.foundation.Canvas
5
5
import androidx.compose.foundation.border
6
- import androidx.compose.foundation.layout.BoxWithConstraints
7
6
import androidx.compose.foundation.layout.Column
8
7
import androidx.compose.foundation.layout.Spacer
9
8
import androidx.compose.foundation.layout.fillMaxSize
10
9
import androidx.compose.foundation.layout.fillMaxWidth
11
10
import androidx.compose.foundation.layout.height
12
11
import androidx.compose.foundation.layout.padding
13
12
import androidx.compose.foundation.layout.size
13
+ import androidx.compose.foundation.rememberScrollState
14
+ import androidx.compose.foundation.verticalScroll
14
15
import androidx.compose.material.Slider
15
16
import androidx.compose.material.Text
16
17
import androidx.compose.runtime.Composable
17
18
import androidx.compose.runtime.getValue
18
19
import androidx.compose.runtime.mutableFloatStateOf
19
20
import androidx.compose.runtime.remember
20
21
import androidx.compose.runtime.setValue
21
- import androidx.compose.ui.Alignment
22
22
import androidx.compose.ui.Modifier
23
23
import androidx.compose.ui.geometry.Offset
24
24
import androidx.compose.ui.graphics.Color
@@ -32,77 +32,137 @@ import java.util.Random
32
32
@Preview
33
33
@Composable
34
34
fun ControlledExplosion () {
35
+
36
+ var progress by remember { mutableFloatStateOf(0f ) }
37
+
38
+ var visibilityThresholdLow by remember {
39
+ mutableFloatStateOf(0f )
40
+ }
41
+
42
+ var visibilityThresholdHigh by remember {
43
+ mutableFloatStateOf(1f )
44
+ }
45
+
46
+ val particleCount = 1
47
+
48
+ val density = LocalDensity .current
49
+
50
+ val sizeDp = with (density) {
51
+ 1000f .toDp()
52
+ }
53
+ val sizePx = with (density) {
54
+ sizeDp.toPx()
55
+ }
56
+ val sizePxHalf = sizePx / 2
57
+
58
+ val particles = remember(
59
+ visibilityThresholdLow,
60
+ visibilityThresholdHigh
61
+ ) {
62
+ List (particleCount) {
63
+ ExplodingParticle (
64
+ color = Color (listOf (0xffea4335 , 0xff4285f4 , 0xfffbbc05 , 0xff34a853 ).random()),
65
+ startXPosition = sizePxHalf.toInt(),
66
+ startYPosition = sizePxHalf.toInt(),
67
+ maxHorizontalDisplacement = sizePxHalf,
68
+ maxVerticalDisplacement = sizePxHalf,
69
+ visibilityThresholdLow = visibilityThresholdLow,
70
+ visibilityThresholdHigh = visibilityThresholdHigh
71
+ )
72
+ }
73
+ }
74
+
35
75
Column (
36
- modifier = Modifier .fillMaxSize().padding(16 .dp),
37
- horizontalAlignment = Alignment .CenterHorizontally
76
+ modifier = Modifier .fillMaxSize()
77
+ .verticalScroll(rememberScrollState())
78
+ .padding(vertical = 16 .dp, horizontal = 8 .dp),
38
79
) {
39
- var progress by remember { mutableFloatStateOf(0f ) }
40
80
41
- Explosion (progress)
81
+ Explosion (
82
+ progress = progress,
83
+ particles = particles,
84
+ sizeDp = sizeDp
85
+ )
42
86
43
87
Spacer (Modifier .height(16 .dp))
44
- Text (text = " Progress: $progress " , fontSize = 18 .sp)
88
+
89
+ val particle = particles.first()
90
+ Text (
91
+ text = " Progress: ${(progress * 100 ).toInt() / 100f } \n " +
92
+ " trajectory: ${(particle.trajectoryProgress * 100 ).toInt() / 100f } \n " +
93
+ " currentTime: ${(particle.currentTime * 100f ).toInt() / 100f } \n " ,
94
+ fontSize = 18 .sp
95
+ )
45
96
Slider (
46
97
modifier = Modifier .fillMaxWidth(),
47
98
value = progress,
48
99
onValueChange = {
49
100
progress = it
50
101
}
51
102
)
103
+
104
+ Text (" visibilityThresholdLow: $visibilityThresholdLow " )
105
+ Slider (
106
+ modifier = Modifier .fillMaxWidth(),
107
+ value = visibilityThresholdLow,
108
+ onValueChange = {
109
+ visibilityThresholdLow = it
110
+ },
111
+ valueRange = 0f .. visibilityThresholdHigh
112
+ )
113
+
114
+ Text (" visibilityThresholdHigh: $visibilityThresholdHigh " )
115
+ Slider (
116
+ modifier = Modifier .fillMaxWidth(),
117
+ value = visibilityThresholdHigh,
118
+ onValueChange = {
119
+ visibilityThresholdHigh = it
120
+ },
121
+ valueRange = visibilityThresholdLow.. 1f
122
+ )
52
123
Spacer (Modifier .height(16 .dp))
53
124
}
54
125
}
55
126
56
127
@Composable
57
128
fun Explosion (
129
+ sizeDp : Dp ,
130
+ particles : List <ExplodingParticle >,
58
131
progress : Float
59
132
) {
60
- BoxWithConstraints {
133
+ val density = LocalDensity .current
134
+ val sizePx = with (density) {
135
+ sizeDp.toPx()
136
+ }
61
137
62
- val sizeDp = maxWidth
63
- val density = LocalDensity .current
64
- val sizePx = with (density) {
65
- sizeDp.toPx()
66
- }
67
- val sizePxHalf = sizePx / 2
68
- val particles = remember {
69
- List (150 ) {
70
- ExplodingParticle (
71
- color = Color (listOf (0xffea4335 , 0xff4285f4 , 0xfffbbc05 , 0xff34a853 ).random()),
72
- startXPosition = sizePxHalf.toInt(),
73
- startYPosition = sizePxHalf.toInt(),
74
- maxHorizontalDisplacement = sizePxHalf,
75
- maxVerticalDisplacement = sizePxHalf
76
- )
77
- }
78
- }
79
- particles.forEach { it.updateProgress(progress) }
80
-
81
- Canvas (
82
- modifier = Modifier
83
- .border(width = 1 .dp, color = Color (0x26000000 ))
84
- .size(sizeDp)
85
- ) {
86
- drawLine(
87
- color = Color .Black ,
88
- start = Offset (sizePxHalf, 0f ),
89
- end = Offset (sizePxHalf, sizePx),
90
- strokeWidth = 2 .dp.toPx()
91
- )
92
- drawLine(
93
- color = Color .Black ,
94
- start = Offset (0f , sizePxHalf),
95
- end = Offset (sizePx, sizePxHalf),
96
- strokeWidth = 2 .dp.toPx()
138
+ val sizePxHalf = sizePx / 2
139
+
140
+ particles.forEach { it.updateProgress(progress) }
141
+
142
+ Canvas (
143
+ modifier = Modifier
144
+ .border(width = 1 .dp, color = Color (0x26000000 ))
145
+ .size(sizeDp)
146
+ ) {
147
+ drawLine(
148
+ color = Color .Black ,
149
+ start = Offset (sizePxHalf, 0f ),
150
+ end = Offset (sizePxHalf, sizePx),
151
+ strokeWidth = 2 .dp.toPx()
152
+ )
153
+ drawLine(
154
+ color = Color .Black ,
155
+ start = Offset (0f , sizePxHalf),
156
+ end = Offset (sizePx, sizePxHalf),
157
+ strokeWidth = 2 .dp.toPx()
158
+ )
159
+ particles.forEach { particle ->
160
+ drawCircle(
161
+ alpha = particle.alpha,
162
+ color = particle.color,
163
+ radius = 5 .dp.toPx(),
164
+ center = Offset (particle.currentXPosition, particle.currentYPosition),
97
165
)
98
- particles.forEach { particle ->
99
- drawCircle(
100
- alpha = particle.alpha,
101
- color = particle.color,
102
- radius = 5 .dp.toPx(),
103
- center = Offset (particle.currentXPosition, particle.currentYPosition),
104
- )
105
- }
106
166
}
107
167
}
108
168
}
@@ -112,7 +172,9 @@ class ExplodingParticle(
112
172
val startXPosition : Int ,
113
173
val startYPosition : Int ,
114
174
val maxHorizontalDisplacement : Float ,
115
- val maxVerticalDisplacement : Float
175
+ val maxVerticalDisplacement : Float ,
176
+ val visibilityThresholdLow : Float ,
177
+ val visibilityThresholdHigh : Float
116
178
) {
117
179
private val velocity = 4 * maxVerticalDisplacement
118
180
private val acceleration = - 2 * velocity
@@ -121,13 +183,38 @@ class ExplodingParticle(
121
183
122
184
var alpha = 1f
123
185
186
+ var currentTime: Float = 0f
187
+ private set
188
+ var trajectoryProgress: Float = 0f
189
+ private set
190
+
124
191
fun updateProgress (explosionProgress : Float ) {
125
- val currentTime = explosionProgress * 1f
192
+
193
+ // Trajectory progress translates progress from 0f-1f to
194
+ // visibilityThresholdLow-visibilityThresholdHigh
195
+ // range. For instance, 0.1-0.6f range movement starts when
196
+ // explosionProgress is at 0.1f and reaches 1f
197
+ // when explosionProgress reaches 0.6f and trajectoryProgress .
198
+
199
+ // Each 0.1f change in trajectoryProgress 0.5f total range
200
+ // corresponds to 0.2f change of current time
201
+ trajectoryProgress =
202
+ if (explosionProgress < visibilityThresholdLow ||
203
+ (explosionProgress > visibilityThresholdHigh)
204
+ ) {
205
+ return
206
+ } else {
207
+ explosionProgress
208
+ .mapInRange(visibilityThresholdLow, visibilityThresholdHigh, 0f , 1f )
209
+ }
210
+
211
+ currentTime = trajectoryProgress
212
+ // .mapInRange(0f, 1f, 0f, 1.4f)
126
213
127
214
val verticalDisplacement =
128
215
currentTime * velocity + 0.5 * acceleration * currentTime * currentTime
129
216
130
- currentXPosition = startXPosition + maxHorizontalDisplacement * explosionProgress
217
+ currentXPosition = startXPosition + maxHorizontalDisplacement * trajectoryProgress
131
218
currentYPosition = (startYPosition - verticalDisplacement).toFloat()
132
219
133
220
}
0 commit comments