@@ -47,6 +47,19 @@ private data class ConfettiParticle2D(
4747 val rotation : Float
4848)
4949
50+ /* *
51+ * Renders a confetti burst anchored to a given popup rectangle.(checkinpopup).
52+ *
53+ * Reads 'showing' from [ConfettiViewModel]. If false or rect is null, renders nothing.
54+ * When shown, spawns particles inside the bounds of 'originRectInRoot'.
55+ * Animates progress from 0 to 1 in 1.2 seconds with linear easing.
56+ * Applies simple ballistic motion to each particle:
57+ * x(t) = x0 + vx * t
58+ * y(t) = y0 +vy0 * t + 0.5 * g * t^2
59+ * where g = 1750 px/s^2 to pull particles downward.
60+ * Fades particles out as progress approaches 1.
61+ * When complete calls [ConfettiViewModel.onAnimationFinished] to hide the confetti.
62+ */
5063@Composable
5164fun ConfettiBurst (
5265 confettiViewModel : ConfettiViewModel ,
@@ -73,22 +86,31 @@ fun ConfettiBurst(
7386 LaunchedEffect (uiState.showing) {
7487 if (uiState.showing) started = true
7588 }
89+
90+ // Progress 0 to 1 over 1.2s, used as time 't' in the physics below
7691 val progress by animateFloatAsState(
7792 targetValue = if (started) 1f else 0f ,
7893 animationSpec = tween(durationMillis = 1200 , easing = LinearEasing ),
7994 label = " confettiProgress"
8095 )
8196
97+ // build particles each with spawn, shape, size and velocity
8298 val particles = remember((uiState.showing)) {
8399 List (particleCount) {
100+ // spawn uniformly inside the rect bounds
84101 val x = Random .nextFloat() * rect.width + rect.left
85102 val y = Random .nextFloat() * rect.height + rect.top
86103 val start: Offset = Offset (x,y)
104+ // angled straight up with a random right skew to look more natural
87105 val angle = ((- 90f + Random .nextFloat() * 110f ) * Math .PI / 180f ).toFloat()
106+ // initial speed
88107 val speed = Random .nextFloat() * 700f + 400f
108+ // velocity compontents
89109 val vx = cos(angle) * speed
90110 val vy0 = sin(angle) * speed
111+ // size in px
91112 val size = Random .nextInt(18 , 34 ).toFloat()
113+
92114 ConfettiParticle2D (
93115 start = start,
94116 vx = vx,
@@ -109,23 +131,29 @@ fun ConfettiBurst(
109131 }
110132 }
111133
134+ // Renders ballisitc motion + fade out for each particle
112135 Canvas (modifier = modifier.fillMaxSize()) {
136+ // gravity and seconds t
113137 val g = 1750f
114138 val t = progress * 1.2f
115139
116140 particles.forEach { particle ->
141+ // position at time t
117142 val x = particle.start.x + particle.vx * t
118143 val y = particle.start.y + (particle.vy0 * t + 0.5f * g * t * t)
119144
145+ // fade progress
120146 val alpha = 1f - progress
121147
148+ // gradient brush
122149 val brush = Brush .linearGradient(
123150 colors = colors,
124151 start = Offset (x - particle.size * 0.8f , y- particle.size * 0.8f ),
125152 end = Offset (x + particle.size* 0.8f , y+ particle.size * 0.8f )
126153 )
127154
128155 when (particle.shape) {
156+ // draws circle
129157 ConfettiShape .CIRCLE -> {
130158 drawCircle(
131159 brush = brush,
@@ -135,6 +163,7 @@ fun ConfettiBurst(
135163 )
136164 }
137165
166+ // draws rectangle
138167 ConfettiShape .RECTANGLE -> {
139168 withTransform({
140169 rotate(degrees = particle.rotation, pivot = Offset (x, y))
0 commit comments