147 lines
5.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(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;
var lastTouch = 0;
function registerTap(source, target) {
if (active) { console.log('[🎈 easter egg] already active, ignoring'); return; }
tapCount++;
clearTimeout(tapTimer);
tapTimer = setTimeout(function () {
console.log('[🎈 easter egg] tap window expired, resetting count');
tapCount = 0;
}, 3000);
console.log('[🎈 easter egg] tap ' + tapCount + '/5 via ' + source + ' on <' + (target.tagName || '?').toLowerCase() + '>');
if (tapCount >= 5) {
tapCount = 0;
console.log('[🎈 easter egg] launching!');
launch();
}
}
// touchstart for mobile (fires immediately, not affected by scroll)
document.addEventListener('touchstart', function (e) {
if (e.target.closest('input, select, textarea')) {
console.log('[🎈 easter egg] touchstart ignored — form element');
return;
}
lastTouch = Date.now();
registerTap('touch', e.target);
}, { passive: true });
// click for desktop (deduplicated from touch events)
document.addEventListener('click', function (e) {
if (Date.now() - lastTouch < 500) {
console.log('[🎈 easter egg] click deduplicated (touch fired recently)');
return;
}
if (e.target.closest('input, select, textarea')) {
console.log('[🎈 easter egg] click ignored — form element');
return;
}
registerTap('click', e.target);
});
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; // 3870 px
var leftPct = 3 + Math.random() * 94; // 397 %
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.58 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);
}
})();