@@ -21,7 +21,6 @@ import androidx.compose.material.Button
2121import androidx.compose.material.Slider
2222import androidx.compose.material.Text
2323import androidx.compose.runtime.Composable
24- import androidx.compose.runtime.DisposableEffect
2524import androidx.compose.runtime.LaunchedEffect
2625import androidx.compose.runtime.Stable
2726import androidx.compose.runtime.getValue
@@ -46,7 +45,7 @@ import androidx.compose.ui.res.painterResource
4645import androidx.compose.ui.tooling.preview.Preview
4746import androidx.compose.ui.unit.dp
4847import androidx.compose.ui.unit.sp
49- import com.smarttoolfactory.tutorial1_1basics.chapter6_graphics.mapInRange
48+ import com.smarttoolfactory.tutorial1_1basics.chapter6_graphics.scale
5049import com.smarttoolfactory.tutorial1_1basics.chapter6_graphics.toPx
5150import com.smarttoolfactory.tutorial1_1basics.ui.Pink400
5251import 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
232240data 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
256264fun 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
342353class 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 }
0 commit comments