|
1 | 1 | package com.smarttoolfactory.tutorial1_1basics.chapter6_graphics
|
2 | 2 |
|
3 | 3 | import android.graphics.Bitmap
|
| 4 | +import android.media.Image |
4 | 5 | import androidx.compose.animation.core.Animatable
|
5 | 6 | import androidx.compose.animation.core.tween
|
6 | 7 | import androidx.compose.foundation.Canvas
|
@@ -169,6 +170,12 @@ data class Particle(
|
169 | 170 | )
|
170 | 171 | }
|
171 | 172 |
|
| 173 | +class ParticleController( |
| 174 | +) { |
| 175 | + var imageBitmap: ImageBitmap? = null |
| 176 | + val particles = mutableStateListOf<Particle>() |
| 177 | +} |
| 178 | + |
172 | 179 | @Preview
|
173 | 180 | @Composable
|
174 | 181 | fun GraphicsLayerToParticles() {
|
@@ -255,61 +262,99 @@ fun GraphicsLayerToParticles() {
|
255 | 262 | .clickable {
|
256 | 263 | coroutineScope.launch {
|
257 | 264 | animatable.snapTo(0f)
|
258 | | - val startTime = System.currentTimeMillis() |
259 | 265 | animatable.animateTo(
|
260 | 266 | targetValue = 1f,
|
261 | | - animationSpec = tween(duration.toInt()), |
| 267 | + animationSpec = tween( |
| 268 | + durationMillis = duration.toInt(), |
| 269 | +// easing = LinearEasing |
| 270 | + ), |
262 | 271 | block = {
|
263 | 272 |
|
264 | 273 | val progress = this.value
|
265 | 274 |
|
266 | | - val timePassed = System.currentTimeMillis() - startTime |
| 275 | + particleList.forEachIndexed { index, particle -> |
| 276 | + |
| 277 | + if (particle.active) { |
| 278 | + val posX = particle.initialCenter.x |
| 279 | + val posY = particle.initialCenter.y |
267 | 280 |
|
268 | 281 |
|
269 | | - particleList.forEach { particle -> |
| 282 | + val range = progress |
270 | 283 |
|
271 | | - if (particle.active) { |
272 | | - val oldCenter = particle.center |
273 | | - val posX = oldCenter.x |
274 | | - val posY = oldCenter.y |
| 284 | + val columnFraction = |
| 285 | + index / (particleList.size.toFloat()) |
| 286 | + |
| 287 | + val columnSection = columnFraction * 100 |
| 288 | + |
| 289 | + val animate = columnFraction < range |
275 | 290 |
|
276 | | - val newX = posX + 5f * progress * Random.nextFloat() |
277 | | - val newY = posY - 15f * progress * Random.nextFloat() |
| 291 | + val sectionBasedProgress = |
| 292 | + scale( |
| 293 | + a1 = columnFraction, |
| 294 | + b1 = 1f, |
| 295 | + x1 = progress, |
| 296 | + a2 = 0f, |
| 297 | + b2 = 1f |
| 298 | + ) |
278 | 299 |
|
279 | | - particle.center =Offset(newX, newY) |
| 300 | + if (animate) { |
280 | 301 |
|
281 | | - // TODO Decide whether to use time or progress |
282 | | - val timeFraction = timePassed / duration |
283 | 302 |
|
284 | | - val particleDecayFactor = particle.decayFactor |
| 303 | + println( |
| 304 | + "progress: $progress, " + |
| 305 | + "sectionBasedProgress: $sectionBasedProgress, " + |
| 306 | + "columnFraction: $columnFraction, " + |
| 307 | + "columnSection: $columnSection, " + |
| 308 | + "range: $range," + |
| 309 | + " index: $index, " + |
| 310 | + "animate: $animate" |
| 311 | + ) |
285 | 312 |
|
286 | | - val decayFactor = |
287 | | - if (progress < .80f) particleDecayFactor |
288 | | - else if (progress < .9f) particleDecayFactor + 1 |
289 | | - else if (progress < .98f) particleDecayFactor + 3 |
290 | | - else |
291 | | - particleDecayFactor |
292 | | - .coerceAtLeast(5) + 1 |
| 313 | + val velocityX = -30f + (60f) * Random.nextFloat() |
| 314 | + val velocityY = |
| 315 | + -40f * Random.nextInt(100, 150) / 100f |
293 | 316 |
|
294 | | - if (animateSize) { |
295 | | - val radius = particle.radius |
296 | | - val newRadius = |
297 | | - radius - progress * decayFactor * particle.initialRadius / 100f |
298 | 317 |
|
299 | | - particle.radius = newRadius.coerceAtLeast(0f) |
| 318 | + val newX = |
| 319 | + posX + velocityX * sectionBasedProgress |
| 320 | + val newY = posY + velocityY * sectionBasedProgress |
| 321 | + |
| 322 | + particle.center = |
| 323 | + Offset(newX, newY) |
| 324 | + |
| 325 | + val particleDecayFactor = particle.decayFactor |
| 326 | + |
| 327 | + val decayFactor = |
| 328 | + if (progress < .80f) particleDecayFactor |
| 329 | + else if (progress < .85f) particleDecayFactor + 1 |
| 330 | + else if (progress < .9f) particleDecayFactor + 4 |
| 331 | + else |
| 332 | + particleDecayFactor |
| 333 | + .coerceAtLeast(5) + 1 |
| 334 | + |
| 335 | + if (animateSize) { |
| 336 | + val radius = particle.radius |
| 337 | + val newRadius = |
| 338 | + radius - progress * decayFactor * particle.initialRadius / 100f |
| 339 | + |
| 340 | + particle.radius = newRadius.coerceAtLeast(0f) |
300 | 341 |
|
301 | 342 | // println(
|
302 | 343 | // "Time passed: $timePassed, " +
|
303 | 344 | // "timeFraction: $timeFraction, " +
|
304 | 345 | // "newRadius: $newRadius, " +
|
305 | 346 | // "center: ${particle.center}"
|
306 | 347 | // )
|
307 | | - } |
| 348 | + } |
308 | 349 | //
|
309 | | -// if (animateAlpha) { |
310 | | -// particle.alpha -= (1 - progress) * Random.nextFloat() |
311 | | -// .coerceAtMost(.2f) |
312 | | -// } |
| 350 | + if (animateAlpha) { |
| 351 | + particle.alpha -= (progress) * Random.nextFloat() / 20f |
| 352 | + } |
| 353 | + |
| 354 | + if (progress == 1f) { |
| 355 | + particle.alpha = 0f |
| 356 | + } |
| 357 | + } |
313 | 358 | }
|
314 | 359 | }
|
315 | 360 |
|
@@ -422,10 +467,11 @@ fun createParticles(imageBitmap: ImageBitmap, particleSize: Int): List<Particle>
|
422 | 467 | for (posY in 0 until height step particleSize) {
|
423 | 468 |
|
424 | 469 | // TODO Assign these params
|
425 | | - val scale = Random.nextInt(90, 250) / 100f |
| 470 | + val scale = Random.nextInt(95, 110) / 100f |
426 | 471 | // val scale = 1f
|
427 | 472 | val decayFactor = Random.nextInt(10)
|
428 | | - val alpha = Random.nextFloat().coerceAtLeast(.5f) |
| 473 | +// val alpha = Random.nextFloat().coerceAtLeast(.5f) |
| 474 | + val alpha = 1f |
429 | 475 |
|
430 | 476 | // Get pixel at center of this pixel rectangle
|
431 | 477 | // If last pixel is out of image get it from end of the width or height
|
|
0 commit comments