(ctx: CanvasRenderingContext2D, p: Particle)
| 2475 | |
| 2476 | // Draw a single particle (blue - regular, fades) |
| 2477 | const drawParticle = (ctx: CanvasRenderingContext2D, p: Particle) => { |
| 2478 | ctx.save(); |
| 2479 | ctx.translate(p.x, p.y); |
| 2480 | ctx.rotate(p.rotation); |
| 2481 | |
| 2482 | const lifeRatio = p.life / p.maxLife; |
| 2483 | const alpha = p.opacity * lifeRatio; |
| 2484 | |
| 2485 | ctx.fillStyle = `rgba(37, 99, 235, ${alpha})`; |
| 2486 | ctx.strokeStyle = `rgba(100, 160, 255, ${alpha * 0.8})`; |
| 2487 | ctx.lineWidth = 0.5; |
| 2488 | |
| 2489 | if (p.shape === 'circle') { |
| 2490 | ctx.beginPath(); |
| 2491 | ctx.arc(0, 0, p.size * 0.6, 0, Math.PI * 2); |
| 2492 | ctx.closePath(); |
| 2493 | } else if (p.shape === 'triangle') { |
| 2494 | drawRoundedTriangle(ctx, p.size, p.size * 0.3); |
| 2495 | } else { |
| 2496 | drawRoundedSquare(ctx, p.size, p.size * 0.25); |
| 2497 | } |
| 2498 | |
| 2499 | ctx.fill(); |
| 2500 | ctx.stroke(); |
| 2501 | ctx.restore(); |
| 2502 | }; |
| 2503 | |
| 2504 | // Draw bouncy particle (cyan or blue variant) |
| 2505 | const drawBouncyParticle = (ctx: CanvasRenderingContext2D, p: Particle) => { |
no test coverage detected