Confetti Burst
A pop of paper confetti shoots up from an element, tumbles and falls with a little gravity. Perfect for “order placed” or “you did it”.
How to use it
- Paste the snippet just before
</head>. - Add
data-confetti-burstto a button. It bursts on every click.
Options
Add any of these attributes to the same element.
data-trigger="view" | Burst once when it scrolls into view instead of on click. |
data-count | Number of pieces. Default 70. |
data-colors | Comma-separated colours. |
data-spread | How far pieces fly, in px. Default 220. |
From code, animacious.confetti(element) fires a burst, e.g. after a form is sent.
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/anime.umd.min.js"></script>
<script>
(function () {
// Confetti Burst: a pop of paper confetti shoots up from an element, tumbles
// and falls with a little gravity. Great for "order placed" or "you did it".
// Usage: <button data-confetti-burst>Celebrate</button> (bursts on click)
// Optional: data-trigger="view" (burst when it scrolls into view instead),
// data-count (pieces, default 70), data-colors="#a855f7,#facc15,…",
// data-spread (px, default 220).
// Script use: animacious.confetti(element) fires a burst from code.
var DEFAULTS = { count: 70, spread: 220, colors: ['#a855f7', '#6a0dad', '#ec4899', '#facc15', '#38bdf8', '#22c55e'] };
var css =
'[data-confetti-burst]{position:relative}' +
'.cft-piece{position:absolute;left:50%;top:50%;width:8px;height:12px;margin:-6px 0 0 -4px;pointer-events:none;border-radius:1px;z-index:10}';
var style = document.createElement('style');
style.textContent = css;
document.head.appendChild(style);
function burst(el) {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
var d = el.dataset;
var colors = d.colors ? d.colors.split(',').map(function (c) { return c.trim(); }) : DEFAULTS.colors;
var count = +d.count || DEFAULTS.count;
var spread = +d.spread || DEFAULTS.spread;
var R = anime.utils.random;
for (var i = 0; i < count; i++) {
var p = document.createElement('span');
p.className = 'cft-piece';
p.style.background = colors[i % colors.length];
if (i % 3 === 0) p.style.borderRadius = '50%';
el.appendChild(p);
var duration = R(1400, 2400);
var rise = -R(spread * 0.4, spread); // up
var fall = R(spread * 0.2, spread * 0.8); // then down past the start
anime.animate(p, {
x: { to: R(-spread, spread), duration: duration, ease: 'out(3)' },
y: [
{ to: rise, duration: duration * 0.35, ease: 'out(2)' },
{ to: fall, duration: duration * 0.65, ease: 'in(2)' }
],
rotate: { to: R(-720, 720), duration: duration, ease: 'linear' },
rotateX: { to: R(360, 1440), duration: duration, ease: 'linear' },
opacity: [{ to: 1, duration: duration * 0.7 }, { to: 0, duration: duration * 0.3 }],
onComplete: (function (piece) { return function () { piece.remove(); }; })(p)
});
}
}
function init() {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (!e.isIntersecting) return;
io.unobserve(e.target);
burst(e.target);
});
}, { threshold: 0.5 });
document.querySelectorAll('[data-confetti-burst]').forEach(function (el) {
if (el._cft) return;
el._cft = true;
if (el.dataset.trigger === 'view') io.observe(el);
else el.addEventListener('click', function () { burst(el); });
});
}
window.animacious = window.animacious || {};
window.animacious.confetti = burst;
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
else init();
})();
</script><button data-confetti-burst>Place order</button>