| 1 |
import confetti from 'canvas-confetti'; |
| 2 |
|
| 3 |
let confettiInstance = null; |
| 4 |
|
| 5 |
const ensureConfettiInstance = () => { |
| 6 |
if (confettiInstance) return confettiInstance; |
| 7 |
|
| 8 |
// Custom canvas to account for height issues |
| 9 |
const canvas = document.createElement('canvas'); |
| 10 |
canvas.style.position = 'fixed'; |
| 11 |
canvas.style.top = '0'; |
| 12 |
canvas.style.left = '0'; |
| 13 |
canvas.style.width = '100%'; |
| 14 |
canvas.style.height = '100%'; |
| 15 |
canvas.style.pointerEvents = 'none'; |
| 16 |
canvas.style.zIndex = Number.MAX_SAFE_INTEGER; |
| 17 |
document.body.appendChild(canvas); |
| 18 |
|
| 19 |
confettiInstance = confetti.create(canvas, { |
| 20 |
disableForReducedMotion: true, |
| 21 |
resize: true, |
| 22 |
}); |
| 23 |
return confettiInstance; |
| 24 |
}; |
| 25 |
|
| 26 |
export const throwSideConfetti = () => { |
| 27 |
const LEFT = { |
| 28 |
count: 200, |
| 29 |
defaults: { |
| 30 |
origin: { y: 0.7, x: 0 }, |
| 31 |
}, |
| 32 |
shots: [ |
| 33 |
{ ratio: 0.25, spread: 26, startVelocity: 55 }, |
| 34 |
{ ratio: 0.2, spread: 60 }, |
| 35 |
{ ratio: 0.35, spread: 100, decay: 0.91, scalar: 0.8 }, |
| 36 |
{ ratio: 0.1, spread: 120, startVelocity: 25, decay: 0.92, scalar: 1.2 }, |
| 37 |
{ ratio: 0.1, spread: 120, startVelocity: 45 }, |
| 38 |
], |
| 39 |
}; |
| 40 |
throwConfetti(LEFT); |
| 41 |
throwConfetti({ |
| 42 |
...LEFT, |
| 43 |
defaults: { |
| 44 |
origin: { y: 0.7, x: 1 }, |
| 45 |
}, |
| 46 |
}); |
| 47 |
}; |
| 48 |
|
| 49 |
const throwConfetti = (config) => { |
| 50 |
const { count = 1, defaults, shots } = config; |
| 51 |
const shoot = ensureConfettiInstance(); |
| 52 |
|
| 53 |
shots.forEach(({ ratio = 1, ...opts }) => { |
| 54 |
shoot({ |
| 55 |
...(defaults ?? {}), |
| 56 |
...(opts ?? {}), |
| 57 |
particleCount: Math.floor(count * ratio), |
| 58 |
zIndex: Number.MAX_SAFE_INTEGER, |
| 59 |
}); |
| 60 |
}); |
| 61 |
}; |
| 62 |
|