All files / web/src/utils confetti.ts

100% Statements 106/106
100% Branches 8/8
100% Functions 2/2
100% Lines 106/106

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 1071x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 11x 11x 11x 11x 11x 100x 100x 11x 11x 11x 52x 52x 52x 2x 2x 2x 50x 50x 50x 50x 50x 50x 50x 50x 50x 50x 50x 50x 50x 50x 50x 50x 50x 50x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 11x 11x 11x 11x 5x 5x 5x 5x 5x 5x 5x 5x 11x 11x 11x 11x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 11x 11x  
/**
 * Confetti celebration utility
 *
 * Shared confetti function for celebrations throughout the app.
 * Originally from CelebrationProgressionBanner, extracted for reuse.
 */
 
import type { Shape } from 'canvas-confetti'
import confetti from 'canvas-confetti'
 
/**
 * Fire a multi-burst confetti celebration.
 * Duration: ~4 seconds total with bursts, fireworks, and star finale.
 */
export function fireConfettiCelebration(): void {
  const duration = 4000
  const animationEnd = Date.now() + duration
  const defaults = { startVelocity: 30, spread: 360, ticks: 60, zIndex: 10000 }
 
  function randomInRange(min: number, max: number) {
    return Math.random() * (max - min) + min
  }
 
  // Multiple bursts of confetti
  const interval = setInterval(() => {
    const timeLeft = animationEnd - Date.now()
 
    if (timeLeft <= 0) {
      clearInterval(interval)
      return
    }
 
    const particleCount = 50 * (timeLeft / duration)
 
    // Confetti from left side
    confetti({
      ...defaults,
      particleCount,
      origin: { x: randomInRange(0.1, 0.3), y: Math.random() - 0.2 },
      colors: ['#FFD700', '#FFA500', '#FF6347', '#FF1493', '#00CED1', '#32CD32'],
    })
 
    // Confetti from right side
    confetti({
      ...defaults,
      particleCount,
      origin: { x: randomInRange(0.7, 0.9), y: Math.random() - 0.2 },
      colors: ['#FFD700', '#FFA500', '#FF6347', '#FF1493', '#00CED1', '#32CD32'],
    })
  }, 250)
 
  // Initial big burst from center
  confetti({
    particleCount: 100,
    spread: 70,
    origin: { x: 0.5, y: 0.5 },
    colors: ['#FFD700', '#FFA500', '#FF6347'],
    zIndex: 10000,
  })
 
  // Fireworks effect - shooting stars
  setTimeout(() => {
    confetti({
      particleCount: 50,
      angle: 60,
      spread: 55,
      origin: { x: 0, y: 0.8 },
      colors: ['#FFD700', '#FFFF00', '#FFA500'],
      zIndex: 10000,
    })
    confetti({
      particleCount: 50,
      angle: 120,
      spread: 55,
      origin: { x: 1, y: 0.8 },
      colors: ['#FFD700', '#FFFF00', '#FFA500'],
      zIndex: 10000,
    })
  }, 500)
 
  // More fireworks
  setTimeout(() => {
    confetti({
      particleCount: 80,
      angle: 90,
      spread: 100,
      origin: { x: 0.5, y: 0.9 },
      colors: ['#FF1493', '#FF69B4', '#FFB6C1', '#FF6347'],
      zIndex: 10000,
    })
  }, 1000)
 
  // Star burst finale
  setTimeout(() => {
    const shapes: Shape[] = ['star', 'circle']
    confetti({
      particleCount: 150,
      spread: 180,
      origin: { x: 0.5, y: 0.4 },
      colors: ['#FFD700', '#FFA500', '#FF6347', '#FF1493', '#00CED1', '#9370DB'],
      shapes,
      scalar: 1.2,
      zIndex: 10000,
    })
  }, 1500)
}