@@ -41,8 +41,10 @@ import androidx.compose.ui.geometry.Size
4141import androidx.compose.ui.graphics.Color
4242import androidx.compose.ui.graphics.ImageBitmap
4343import androidx.compose.ui.graphics.asAndroidBitmap
44+ import androidx.compose.ui.graphics.asImageBitmap
4445import androidx.compose.ui.graphics.drawscope.DrawScope
4546import androidx.compose.ui.graphics.drawscope.Stroke
47+ import androidx.compose.ui.graphics.drawscope.clipRect
4648import androidx.compose.ui.graphics.rememberGraphicsLayer
4749import androidx.compose.ui.platform.LocalContext
4850import androidx.compose.ui.platform.LocalDensity
@@ -59,7 +61,6 @@ import com.smarttoolfactory.tutorial1_1basics.ui.Pink400
5961import kotlinx.coroutines.CancellationException
6062import kotlin.random.Random
6163
62-
6364@Preview
6465@Composable
6566fun SingleParticleTrajectorySample () {
@@ -219,12 +220,13 @@ fun ParticleAnimationSample() {
219220 .border(2 .dp, Color .Red )
220221 .size(widthDp)
221222 .disintegrate(
222- // progress = progress,
223+ progress = progress,
223224 particleState = particleState,
224225 onStart = {
225226 Toast .makeText(context, " Animation started..." , Toast .LENGTH_SHORT ).show()
226227 },
227228 onEnd = {
229+ // particleState.animationStatus = AnimationStatus.Idle
228230 Toast .makeText(context, " Animation ended..." , Toast .LENGTH_SHORT ).show()
229231 }
230232 ),
@@ -323,22 +325,23 @@ fun Modifier.disintegrate(
323325 }
324326 }
325327
326- Modifier .drawWithCache {
327- onDrawWithContent {
328- if (animationStatus != AnimationStatus .Playing ) {
329- drawContent()
330- graphicsLayer.record {
331- this @onDrawWithContent.drawContent()
328+ Modifier
329+ .drawWithCache {
330+ onDrawWithContent {
331+ if (animationStatus != AnimationStatus .Playing ) {
332+ drawContent()
333+ graphicsLayer.record {
334+ this @onDrawWithContent.drawContent()
335+ }
332336 }
333- }
334337
335- particleState.updateAndDrawParticles(
336- drawScope = this ,
337- particleList = particleState.particleList,
338- progress = progress
339- )
338+ particleState.updateAndDrawParticles(
339+ drawScope = this ,
340+ particleList = particleState.particleList,
341+ progress = progress
342+ )
343+ }
340344 }
341- }
342345}
343346
344347@Composable
@@ -365,6 +368,11 @@ class ParticleState internal constructor(particleSize: Dp) {
365368 var bitmap: Bitmap ? = null
366369 internal set
367370
371+ var animationSpec = tween<Float >(
372+ durationMillis = 2000 ,
373+ easing = FastOutSlowInEasing
374+ )
375+
368376 fun addParticle (particle : Particle ) {
369377 particleList.add(particle)
370378 }
@@ -377,6 +385,15 @@ class ParticleState internal constructor(particleSize: Dp) {
377385 with (drawScope) {
378386 if (animationStatus != AnimationStatus .Idle ) {
379387
388+ // TODO disintegrate image non-uniformly, with blend mode of particles
389+ // clipRect(
390+ // left = progress * size.width
391+ // ) {
392+ // bitmap?.asImageBitmap()?.let {
393+ // drawImage(it)
394+ // }
395+ // }
396+
380397 particleList.forEach { particle ->
381398
382399 updateParticle(progress, particle)
@@ -394,13 +411,6 @@ class ParticleState internal constructor(particleSize: Dp) {
394411 )
395412 }
396413
397- // TODO disintegrate image non-uniformly, with blend mode of particles
398- // clipRect(
399- // left = progress * size.width
400- // ) {
401- // this@onDrawWithContent.drawContent()
402- // }
403-
404414 // For debugging
405415 drawRect(
406416 color = Color .Black ,
@@ -422,7 +432,6 @@ class ParticleState internal constructor(particleSize: Dp) {
422432 val width = bitmap.width
423433 val height = bitmap.height
424434
425-
426435 val particleRadius = particleSize / 2
427436
428437 // divide image into squares based on particle size
@@ -467,9 +476,11 @@ class ParticleState internal constructor(particleSize: Dp) {
467476 // Add some vertical randomization for trajectory so particles don't start
468477 // animating vertically as well. Particles with smaller y value in same x
469478 // coordinate tend to start earlier
479+ val startYOffset =
480+ (fractionToImageHeight * Random .nextFloat()).coerceAtMost(.2f )
481+
470482 trajectoryProgressRange =
471- trajectoryProgressRange.start + fractionToImageHeight * Random .nextFloat()
472- .coerceAtMost(.2f ).. trajectoryProgressRange.endInclusive
483+ trajectoryProgressRange.start + startYOffset.. trajectoryProgressRange.endInclusive
473484
474485 val endSize = randomInRange(0f , particleSize.toFloat() * .5f )
475486
@@ -565,13 +576,14 @@ class ParticleState internal constructor(particleSize: Dp) {
565576 try {
566577 onStart()
567578 animatable.snapTo(0f )
568- animatable.animateTo(1f , tween(durationMillis = 2400 , easing = FastOutSlowInEasing ))
569- animationStatus = AnimationStatus .Idle
579+ animatable.animateTo(
580+ targetValue = 1f ,
581+ animationSpec = animationSpec
582+ )
570583 } catch (e: CancellationException ) {
571584 println (" FAILED: ${e.message} " )
572585 } finally {
573586 onEnd()
574- animationStatus = AnimationStatus .Idle
575587 }
576588 }
577589
0 commit comments