Add fireworks effect on ticket CTA clicks

Canvas-based particle system that triggers when clicking any
href=#tickets button. Uses theme colors, auto-clears when done.
This commit is contained in:
Tony M 2026-07-04 11:33:33 -07:00
parent dd1855b20e
commit 02ae132a7a

95
js/fireworks.js Normal file
View File

@ -0,0 +1,95 @@
(function () {
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.style.cssText =
"position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9999;";
document.body.appendChild(canvas);
var W = (canvas.width = window.innerWidth);
var H = (canvas.height = window.innerHeight);
window.addEventListener("resize", function () {
W = canvas.width = window.innerWidth;
H = canvas.height = window.innerHeight;
});
var particles = [];
var raf = null;
var colors = ["#3a6b5e", "#c8a24a", "#e0c068", "#5a9384", "#f5e4cf", "#2d5448"];
function Particle(x, y, color) {
this.x = x;
this.y = y;
this.color = color;
var angle = Math.random() * Math.PI * 2;
var speed = Math.random() * 6 + 2;
this.vx = Math.cos(angle) * speed;
this.vy = Math.sin(angle) * speed;
this.alpha = 1;
this.decay = Math.random() * 0.015 + 0.01;
this.size = Math.random() * 3 + 1;
}
Particle.prototype.update = function () {
this.x += this.vx;
this.y += this.vy;
this.vy += 0.08;
this.vx *= 0.99;
this.vy *= 0.99;
this.alpha -= this.decay;
};
Particle.prototype.draw = function () {
ctx.globalAlpha = this.alpha;
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fill();
};
function explode(x, y) {
for (var i = 0; i < 80; i++) {
particles.push(new Particle(x, y, colors[Math.floor(Math.random() * colors.length)]));
}
if (!raf) loop();
}
function loop() {
raf = requestAnimationFrame(loop);
ctx.clearRect(0, 0, W, H);
for (var i = particles.length - 1; i >= 0; i--) {
var p = particles[i];
p.update();
p.draw();
if (p.alpha <= 0) particles.splice(i, 1);
}
if (particles.length === 0) {
cancelAnimationFrame(raf);
raf = null;
ctx.clearRect(0, 0, W, H);
}
}
function handleClick(e) {
var x = e.clientX;
var y = e.clientY;
explode(x, y);
setTimeout(function () { explode(x - 80, y - 60); }, 120);
setTimeout(function () { explode(x + 80, y - 40); }, 240);
setTimeout(function () { explode(x, y - 100); }, 360);
}
function isTicketsCTA(el) {
if (!el) return false;
do {
if (el.tagName === "A" && el.getAttribute("href") === "#tickets") return true;
el = el.parentElement;
} while (el);
return false;
}
document.addEventListener("click", function (e) {
if (isTicketsCTA(e.target)) handleClick(e);
});
})();