@@ -5,6 +5,7 @@ import androidx.compose.animation.core.Animatable
55import androidx.compose.animation.core.tween
66import androidx.compose.foundation.Canvas
77import androidx.compose.foundation.Image
8+ import androidx.compose.foundation.border
89import androidx.compose.foundation.clickable
910import androidx.compose.foundation.gestures.detectTapGestures
1011import androidx.compose.foundation.layout.Arrangement
@@ -59,21 +60,6 @@ enum class Direction {
5960 Top , TopStart , TopEnd , Bottom , BottomStart , BottomEnd
6061}
6162
62- class Particle (var x : Int , var y : Int , val width : Int , val height : Int , val color : Color ) {
63-
64- var alpha: Float = 1f
65-
66- val center: Offset
67- get() = Offset (x + width / 2f , y + height / 2f )
68-
69- var radius: Float = width.coerceAtMost(height) / 2f
70-
71- val rect: Rect
72- get() = Rect (
73- offset = Offset (x.toFloat(), y.toFloat()),
74- size = Size (width.toFloat(), height.toFloat())
75- )
76- }
7763
7864@Preview
7965@Composable
@@ -156,6 +142,38 @@ fun GraphicsLayerToImageBitmapSample() {
156142 }
157143}
158144
145+ data class Particle (
146+ val initialCenter : Offset ,
147+ val initialSize : Size ,
148+ val color : Color ,
149+ val lifeSpan : Float = 1f ,
150+ val scale : Float = 1f
151+ ) {
152+
153+ val initialRadius: Float = initialSize.width.coerceAtMost(initialSize.height) / 2f
154+ var radius: Float = scale * initialRadius
155+
156+ var alpha: Float = 1f
157+
158+
159+ var center: Offset = initialCenter
160+
161+ val initialRect: Rect
162+ get() = Rect (
163+ offset = Offset (
164+ x = initialCenter.x - initialRadius,
165+ y = initialCenter.y - initialRadius
166+ ),
167+ size = initialSize
168+ )
169+
170+ val rect: Rect
171+ get() = Rect (
172+ offset = Offset (center.x - radius, center.y - radius),
173+ size = Size (radius, radius)
174+ )
175+ }
176+
159177@Preview
160178@Composable
161179fun GraphicsLayerToParticles () {
@@ -248,32 +266,42 @@ fun GraphicsLayerToParticles() {
248266 if (particleList.isEmpty().not ()) {
249267 Canvas (
250268 modifier = Modifier
269+ .border(1 .dp, Color .Blue )
251270 .clickable {
252271 coroutineScope.launch {
253- animatable.snapTo(1f )
272+ animatable.snapTo(0f )
273+ var currentTime = System .currentTimeMillis()
254274 animatable.animateTo(
255- targetValue = 0f ,
275+ targetValue = 1f ,
256276 animationSpec = tween(duration.toInt()),
257277 block = {
278+
258279 val progress = this .value
259- particleList.forEachIndexed { index, particle ->
260- particle.x =
261- (particle.x + 5f * progress * Random .nextFloat()).toInt()
262280
263- particle.y =
264- (particle.y - 20f * progress * Random .nextFloat()).toInt( )
281+ val timePassed = System .currentTimeMillis() - currentTime
282+ println ( " Time passed: $timePassed " )
265283
284+ particleList.forEach { particle ->
266285
267- if (animateSize) {
268- val newRadius =
269- (particle.radius - (1 - progress) * Random .nextInt(2 ))
270- particle.radius = newRadius
286+ val oldCenter = particle.center
287+ val posX = oldCenter.x
288+ val posY = oldCenter.y
271289
272- }
273- if (animateAlpha) {
274- particle.alpha - = (1 - progress) * Random .nextFloat()
275- .coerceAtMost(.2f )
276- }
290+ val newX = posX + 5f * progress * Random .nextFloat()
291+ val newY = posY - 5f * progress * Random .nextFloat()
292+
293+ particle.center = Offset (newX, newY)
294+
295+ // if (animateSize) {
296+ // val newRadius =
297+ // particle.radius * (particle.lifeSpan - progress)
298+ // .coerceAtLeast(0f)
299+ // }
300+ //
301+ // if (animateAlpha) {
302+ // particle.alpha -= (1 - progress) * Random.nextFloat()
303+ // .coerceAtMost(.2f)
304+ // }
277305 }
278306 }
279307 )
@@ -300,7 +328,7 @@ fun GraphicsLayerToParticles() {
300328 particleList.forEach { particle: Particle ->
301329
302330 // For debugging borders of particles
303- // val rect = particle.rect
331+ // val rect = particle.initialRect
304332// drawRect(
305333// color = Color.Red,
306334// topLeft = rect.topLeft,
@@ -310,7 +338,7 @@ fun GraphicsLayerToParticles() {
310338
311339 drawCircle(
312340 color = particle.color.copy(alpha = particle.alpha),
313- radius = particle.radius * 1f ,
341+ radius = particle.radius,
314342 center = particle.center,
315343 )
316344 }
@@ -323,7 +351,7 @@ fun GraphicsLayerToParticles() {
323351 onValueChange = {
324352 particleSize = it
325353 },
326- valueRange = 2f .. 50f
354+ valueRange = 2f .. 100f
327355 )
328356
329357 Text (" Duration: ${duration.toInt()} " , fontSize = 22 .sp)
@@ -370,22 +398,41 @@ fun createParticles(imageBitmap: ImageBitmap, particleSize: Int): List<Particle>
370398 " columnCount: $columnCount , rowCount: $rowCount "
371399 )
372400
401+ val particleRadius = particleSize / 2
402+
403+ // divide image into squares based on particle size
404+ // 110x100x image is divided into 10x10 squares
405+
373406 for (posX in 0 until width step particleSize) {
374407 for (posY in 0 until height step particleSize) {
375408
376- val pixel: Int = bitmap.getPixel(posX, posY)
377- val color = Color (pixel)
409+ // Get pixel at center of this pixel rectangle
410+ // If last pixel is out of image get it from end of the width or height
411+ // 🔥x must be < bitmap.width() and y must be < bitmap.height()
412+ val pixelCenterX = (posX + particleRadius).coerceAtMost(width - 1 )
413+ val pixelCenterY = (posY + particleRadius).coerceAtMost(height - 1 )
414+
415+ // println(
416+ // "posX: $posX, pixelCenterX: $pixelCenterX, " +
417+ // "posY: $posY, pixelCenterY: $pixelCenterY"
418+ // )
378419
379- println (" posX: $posX , posY: $posY , color: $color " )
420+ val pixel: Int = bitmap.getPixel(pixelCenterX, pixelCenterY)
421+ val color = Color (pixel)
380422
381423 if (color != Color .Unspecified ) {
424+ val size = particleSize * 1f
425+
382426 particleList.add(
383427 Particle (
384- x = posX,
385- y = posY,
386- width = particleSize,
387- height = particleSize,
388- color = color
428+ initialCenter = Offset (
429+ x = pixelCenterX.toFloat(),
430+ y = pixelCenterY.toFloat()
431+ ),
432+ initialSize = Size (size, size),
433+ color = color,
434+ scale = Random .nextInt(50 , 150 ) / 100f ,
435+ lifeSpan = Random .nextFloat().coerceAtLeast(.5f )
389436 )
390437 )
391438 } else {
0 commit comments