Long press on the logo link was unreliable. Switch to detecting 7 rapid taps on any non-link/button area within 2 seconds — works on mobile and desktop without conflicting with navigation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
122 lines
4.4 KiB
JavaScript
122 lines
4.4 KiB
JavaScript
(function () {
|
||
var COLORS = ['#11b3be','#ff6b9d','#ffd93d','#6bcb77','#4d96ff','#ff6b35','#c77dff','#ff9f43','#54a0ff','#ff6348'];
|
||
|
||
var SVG_NS = 'http://www.w3.org/2000/svg';
|
||
function makeBalloon(color, size) {
|
||
var svg = document.createElementNS(SVG_NS, 'svg');
|
||
svg.setAttribute('viewBox', '0 0 50 82');
|
||
svg.setAttribute('width', size);
|
||
svg.setAttribute('height', Math.round(size * 1.64));
|
||
|
||
var body = document.createElementNS(SVG_NS, 'ellipse');
|
||
body.setAttribute('cx', '25'); body.setAttribute('cy', '26');
|
||
body.setAttribute('rx', '21'); body.setAttribute('ry', '25');
|
||
body.setAttribute('fill', color); body.setAttribute('opacity', '0.88');
|
||
|
||
// shine
|
||
var shine = document.createElementNS(SVG_NS, 'ellipse');
|
||
shine.setAttribute('cx', '18'); shine.setAttribute('cy', '16');
|
||
shine.setAttribute('rx', '7'); shine.setAttribute('ry', '5');
|
||
shine.setAttribute('fill', 'white'); shine.setAttribute('opacity', '0.25');
|
||
shine.setAttribute('transform', 'rotate(-20 18 16)');
|
||
|
||
var knot = document.createElementNS(SVG_NS, 'path');
|
||
knot.setAttribute('d', 'M25 51 Q27.5 55.5 25 58.5 Q22.5 55.5 25 51Z');
|
||
knot.setAttribute('fill', color); knot.setAttribute('opacity', '0.88');
|
||
|
||
var str = document.createElementNS(SVG_NS, 'path');
|
||
str.setAttribute('d', 'M25 58.5 Q20 68 25 80');
|
||
str.setAttribute('stroke', '#999'); str.setAttribute('stroke-width', '1.2');
|
||
str.setAttribute('fill', 'none'); str.setAttribute('opacity', '0.7');
|
||
|
||
svg.appendChild(body);
|
||
svg.appendChild(shine);
|
||
svg.appendChild(knot);
|
||
svg.appendChild(str);
|
||
return svg;
|
||
}
|
||
|
||
var active = false;
|
||
var tapCount = 0;
|
||
var tapTimer = null;
|
||
|
||
// 7 quick taps on any non-interactive part of the page triggers it
|
||
document.addEventListener('click', function (e) {
|
||
if (active) return;
|
||
if (e.target.closest('a, button, input, select, textarea, label')) return;
|
||
|
||
tapCount++;
|
||
clearTimeout(tapTimer);
|
||
tapTimer = setTimeout(function () { tapCount = 0; }, 2000);
|
||
|
||
if (tapCount >= 7) {
|
||
tapCount = 0;
|
||
launch();
|
||
}
|
||
});
|
||
|
||
function launch() {
|
||
active = true;
|
||
|
||
// Inject keyframes once
|
||
if (!document.getElementById('bpb-balloon-style')) {
|
||
var style = document.createElement('style');
|
||
style.id = 'bpb-balloon-style';
|
||
style.textContent = [
|
||
'@keyframes bpb-float {',
|
||
' 0% { transform: translateX(var(--bx)) translateY(0) rotate(var(--br)); opacity: 0; }',
|
||
' 8% { opacity: 1; }',
|
||
' 85% { opacity: 1; }',
|
||
' 100% { transform: translateX(calc(var(--bx) * -1)) translateY(var(--by)) rotate(calc(var(--br) * -0.5)); opacity: 0; }',
|
||
'}',
|
||
].join('\n');
|
||
document.head.appendChild(style);
|
||
}
|
||
|
||
var container = document.createElement('div');
|
||
Object.assign(container.style, {
|
||
position: 'fixed',
|
||
inset: '0',
|
||
pointerEvents: 'none',
|
||
zIndex: '9', // below Bulma navbar (z-index 30)
|
||
overflow: 'hidden',
|
||
});
|
||
document.body.insertBefore(container, document.body.firstChild);
|
||
|
||
var count = 22;
|
||
for (var i = 0; i < count; i++) {
|
||
(function (idx) {
|
||
setTimeout(function () {
|
||
var color = COLORS[Math.floor(Math.random() * COLORS.length)];
|
||
var size = 38 + Math.random() * 32; // 38–70 px
|
||
var leftPct = 3 + Math.random() * 94; // 3–97 %
|
||
var sway = (Math.random() * 40 - 20) + 'px'; // ±20 px horizontal drift
|
||
var tilt = (Math.random() * 24 - 12) + 'deg';
|
||
var dur = 4500 + Math.random() * 3500; // 4.5–8 s
|
||
var riseAmt = -(window.innerHeight + size * 2); // rise full viewport height
|
||
|
||
var wrap = document.createElement('div');
|
||
Object.assign(wrap.style, {
|
||
position: 'absolute',
|
||
bottom: (-size * 1.64 - 10) + 'px',
|
||
left: leftPct + '%',
|
||
'--bx': sway,
|
||
'--by': riseAmt + 'px',
|
||
'--br': tilt,
|
||
animation: 'bpb-float ' + dur + 'ms cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards',
|
||
animationDelay: (idx * 120 + Math.random() * 200) + 'ms',
|
||
});
|
||
|
||
wrap.appendChild(makeBalloon(color, size));
|
||
container.appendChild(wrap);
|
||
}, 0);
|
||
})(i);
|
||
}
|
||
|
||
setTimeout(function () {
|
||
container.remove();
|
||
active = false;
|
||
}, 12000);
|
||
}
|
||
})();
|