Skip to content

Commit c4edb49

Browse files
update particle samples
1 parent 695d281 commit c4edb49

File tree

2 files changed

+68
-43
lines changed

2 files changed

+68
-43
lines changed

Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/ParticleAnimations.kt

Lines changed: 61 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import androidx.compose.material.Button
2121
import androidx.compose.material.Slider
2222
import androidx.compose.material.Text
2323
import androidx.compose.runtime.Composable
24-
import androidx.compose.runtime.DisposableEffect
2524
import androidx.compose.runtime.LaunchedEffect
2625
import androidx.compose.runtime.Stable
2726
import androidx.compose.runtime.getValue
@@ -46,7 +45,7 @@ import androidx.compose.ui.res.painterResource
4645
import androidx.compose.ui.tooling.preview.Preview
4746
import androidx.compose.ui.unit.dp
4847
import androidx.compose.ui.unit.sp
49-
import com.smarttoolfactory.tutorial1_1basics.chapter6_graphics.mapInRange
48+
import com.smarttoolfactory.tutorial1_1basics.chapter6_graphics.scale
5049
import com.smarttoolfactory.tutorial1_1basics.chapter6_graphics.toPx
5150
import com.smarttoolfactory.tutorial1_1basics.ui.Pink400
5251
import kotlinx.coroutines.CancellationException
@@ -58,11 +57,11 @@ fun SingleParticleTrajectorySample() {
5857

5958
var progress by remember { mutableFloatStateOf(0f) }
6059

61-
var visibilityThresholdLow by remember {
60+
var trajectoryProgressStart by remember {
6261
mutableFloatStateOf(0f)
6362
}
6463

65-
var visibilityThresholdHigh by remember {
64+
var trajectoryProgressEnd by remember {
6665
mutableFloatStateOf(1f)
6766
}
6867

@@ -80,7 +79,7 @@ fun SingleParticleTrajectorySample() {
8079

8180
val particleState = rememberParticleState()
8281

83-
LaunchedEffect(visibilityThresholdLow, visibilityThresholdHigh) {
82+
LaunchedEffect(trajectoryProgressStart, trajectoryProgressEnd) {
8483
particleState.particleList.clear()
8584
particleState.addParticle(
8685
Particle(
@@ -91,18 +90,17 @@ fun SingleParticleTrajectorySample() {
9190
),
9291
initialSize = Size(5.dp.toPx(), 5.dp.toPx()),
9392
endSize = Size(sizePx, sizePx),
94-
finalCenter = Offset(
93+
displacement = Offset(
9594
sizePxHalf,
9695
sizePxHalf
9796
),
98-
trajectoryProgressRange = visibilityThresholdLow..visibilityThresholdHigh,
97+
trajectoryProgressRange = trajectoryProgressStart..trajectoryProgressEnd,
9998
row = 0,
10099
column = 0
101100
)
102101
)
103102
}
104103

105-
106104
Column(
107105
modifier = Modifier.fillMaxSize()
108106
.verticalScroll(rememberScrollState())
@@ -158,22 +156,22 @@ fun SingleParticleTrajectorySample() {
158156
}
159157
)
160158

161-
Text("visibilityThresholdLow: $visibilityThresholdLow")
159+
Text("trajectoryProgressStart: $trajectoryProgressStart")
162160
Slider(
163161
modifier = Modifier.fillMaxWidth(),
164-
value = visibilityThresholdLow,
162+
value = trajectoryProgressStart,
165163
onValueChange = {
166-
visibilityThresholdLow = it.coerceAtMost(visibilityThresholdHigh)
164+
trajectoryProgressStart = it.coerceAtMost(trajectoryProgressEnd)
167165
},
168166
valueRange = 0f..1f
169167
)
170168

171-
Text("visibilityThresholdHigh: $visibilityThresholdHigh")
169+
Text("trajectoryProgressEnd: $trajectoryProgressEnd")
172170
Slider(
173171
modifier = Modifier.fillMaxWidth(),
174-
value = visibilityThresholdHigh,
172+
value = trajectoryProgressEnd,
175173
onValueChange = {
176-
visibilityThresholdHigh = it.coerceAtLeast(visibilityThresholdLow)
174+
trajectoryProgressEnd = it.coerceAtLeast(trajectoryProgressStart)
177175
},
178176
valueRange = 0f..1f
179177
)
@@ -188,7 +186,7 @@ fun ParticleAnimationSample() {
188186
modifier = Modifier
189187
.fillMaxSize()
190188
.verticalScroll(rememberScrollState())
191-
.padding(16.dp),
189+
.padding(horizontal = 16.dp, vertical = 32.dp),
192190
verticalArrangement = Arrangement.spacedBy(8.dp)
193191
) {
194192

@@ -201,11 +199,15 @@ fun ParticleAnimationSample() {
201199

202200
val context = LocalContext.current
203201

202+
var progress by remember {
203+
mutableFloatStateOf(0f)
204+
}
204205
Image(
205206
painter = painterResource(R.drawable.avatar_2_raster),
206207
modifier = Modifier
207208
.border(2.dp, Color.Red)
208209
.explode(
210+
progress = progress,
209211
particleState = particleState,
210212
onStart = {
211213
Toast.makeText(context, "Animation started...", Toast.LENGTH_SHORT).show()
@@ -218,6 +220,12 @@ fun ParticleAnimationSample() {
218220
contentDescription = null
219221
)
220222

223+
Slider(
224+
value = progress,
225+
onValueChange = {
226+
progress = it
227+
}
228+
)
221229
Button(
222230
modifier = Modifier.fillMaxWidth(),
223231
onClick = {
@@ -231,12 +239,12 @@ fun ParticleAnimationSample() {
231239

232240
data class Particle(
233241
val initialCenter: Offset,
234-
val finalCenter: Offset,
242+
val displacement: Offset,
235243
val initialSize: Size,
236244
val endSize: Size,
237245
val color: Color,
238246
val trajectoryProgressRange: ClosedRange<Float> = 0f..1f,
239-
val velocity: Float = 4 * finalCenter.y,
247+
val velocity: Float = 4 * displacement.y,
240248
val acceleration: Float = -2 * velocity,
241249
val column: Int,
242250
val row: Int
@@ -254,6 +262,7 @@ data class Particle(
254262
}
255263

256264
fun Modifier.explode(
265+
progress: Float,
257266
particleState: ParticleState,
258267
onStart: () -> Unit = {},
259268
onEnd: () -> Unit = {}
@@ -305,11 +314,13 @@ fun Modifier.explode(
305314

306315
if (animationStatus != AnimationStatus.Idle) {
307316

308-
val progress = particleState.progress
317+
// val progress = particleState.progress
309318

310319
particleState.particleList.forEach { particle ->
311320

312-
// particleState.updateParticle(progress, particle)
321+
if (progress > 0 && progress <= 1f) {
322+
particleState.updateParticle(progress, particle)
323+
}
313324

314325
val color = particle.color
315326
val radius = particle.currentSize.width / 2f
@@ -341,7 +352,7 @@ fun rememberParticleState(): ParticleState {
341352
@Stable
342353
class ParticleState internal constructor() {
343354

344-
var particleSize by mutableStateOf(50.dp)
355+
var particleSize by mutableStateOf(200.dp)
345356

346357
val animatable = Animatable(0f)
347358
val particleList = mutableStateListOf<Particle>()
@@ -396,17 +407,23 @@ class ParticleState internal constructor() {
396407
if (color != Color.Unspecified) {
397408

398409
val initialCenter = Offset(pixelCenterX.toFloat(), pixelCenterY.toFloat())
410+
val horizontalDisplacement = width / 2f
411+
val verticalDisplacement = height / 2f
412+
413+
val velocity = 4 * verticalDisplacement
414+
val acceleration = -2 * velocity
415+
399416
particleList.add(
400417
Particle(
401418
initialCenter = initialCenter,
402-
finalCenter = initialCenter.plus(Offset(width / 2f, -height / 2f)),
419+
displacement = Offset(horizontalDisplacement, verticalDisplacement),
403420
initialSize = Size(particleSize.toFloat(), particleSize.toFloat()),
404421
endSize = Size.Zero,
405422
color = color,
406423
column = column,
407424
row = row,
408-
velocity = 0f,
409-
acceleration = 0f
425+
velocity = velocity,
426+
acceleration = acceleration
410427
)
411428
)
412429

@@ -416,7 +433,7 @@ class ParticleState internal constructor() {
416433
}
417434
}
418435

419-
// println("PARTICLE count: ${particleList.size}")
436+
println("PARTICLE count: ${particleList.size}")
420437

421438
}
422439

@@ -432,22 +449,27 @@ class ParticleState internal constructor() {
432449
// Each 0.1f change in trajectoryProgress 0.5f total range
433450
// corresponds to 0.2f change of current time
434451

435-
val visibilityThresholdLow = trajectoryProgressRange.start
436-
val visibilityThresholdHigh = trajectoryProgressRange.endInclusive
452+
val trajectoryProgressStart = trajectoryProgressRange.start
453+
val trajectoryProgressEnd = trajectoryProgressRange.endInclusive
437454

438455
val startXPosition = initialCenter.x
439456
val startYPosition = initialCenter.x
440457

441-
val maxHorizontalDisplacement = finalCenter.x
458+
val maxHorizontalDisplacement = displacement.x
442459

443460
trajectoryProgress =
444-
if (explosionProgress < visibilityThresholdLow) {
461+
if (explosionProgress < trajectoryProgressStart) {
445462
0f
446-
} else if (explosionProgress > visibilityThresholdHigh) {
463+
} else if (explosionProgress > trajectoryProgressEnd) {
447464
1f
448465
} else {
449-
explosionProgress
450-
.mapInRange(visibilityThresholdLow, visibilityThresholdHigh, 0f, 1f)
466+
scale(
467+
a1 = trajectoryProgressStart,
468+
b1 = trajectoryProgressEnd,
469+
x1 = explosionProgress,
470+
a2 = 0f,
471+
b2 = 1f
472+
)
451473
}
452474

453475
currentTime = trajectoryProgress
@@ -457,12 +479,16 @@ class ParticleState internal constructor() {
457479
// alpha = if (trajectoryProgress < .7f) 1f else
458480
// scale(.7f, 1f, trajectoryProgress, 1f, 0f)
459481

482+
val horizontalDisplacement = maxHorizontalDisplacement * trajectoryProgress
483+
460484
val verticalDisplacement =
461-
currentTime * velocity + 0.5 * acceleration * currentTime * currentTime
485+
velocity * currentTime + 0.5f * acceleration * currentTime * currentTime
486+
487+
println("horizontalDisplacement: $horizontalDisplacement, verticalDisplacement: $verticalDisplacement")
462488

463489
currentPosition = Offset(
464-
x = startXPosition + maxHorizontalDisplacement * trajectoryProgress,
465-
y = (startYPosition - verticalDisplacement).toFloat()
490+
x = startXPosition + horizontalDisplacement,
491+
y = startYPosition - verticalDisplacement
466492
)
467493
}
468494
}
@@ -475,7 +501,7 @@ class ParticleState internal constructor() {
475501
try {
476502
animatable.snapTo(0f)
477503
animatable.animateTo(1f, tween(2000))
478-
animationStatus = AnimationStatus.Idle
504+
// animationStatus = AnimationStatus.Idle
479505
} catch (e: CancellationException) {
480506
println("FAILED: ${e.message}")
481507
}

Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/chapter6_graphics/ParticlePhysics.kt

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ fun ControlledExplosion() {
4343
mutableFloatStateOf(1f)
4444
}
4545

46-
4746
val particleCount = 1
4847

4948
val density = LocalDensity.current
@@ -219,8 +218,13 @@ class ExplodingParticle(
219218
} else if (explosionProgress > visibilityThresholdHigh) {
220219
1f
221220
} else {
222-
explosionProgress
223-
.mapInRange(visibilityThresholdLow, visibilityThresholdHigh, 0f, 1f)
221+
scale(
222+
a1 = visibilityThresholdLow,
223+
b1 = visibilityThresholdHigh,
224+
x1 = explosionProgress,
225+
a2 = 0f,
226+
b2 = 1f
227+
)
224228
}
225229

226230
currentTime = trajectoryProgress
@@ -240,11 +244,6 @@ class ExplodingParticle(
240244
}
241245
}
242246

243-
fun Float.mapInRange(inMin: Float, inMax: Float, outMin: Float, outMax: Float): Float {
244-
return outMin + (((this - inMin) / (inMax - inMin)) * (outMax - outMin))
245-
}
246-
247-
fun Int.dpToPx() = toFloat().dpToPx()
248247
fun Dp.toPx() = value.dpToPx()
249248

250249
fun Float.dpToPx() = this * Resources.getSystem().displayMetrics.density

0 commit comments

Comments
 (0)