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 107 | 1x 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)
}
|