// Matrix rain background effect — header-scoped build for packages.danix.xyz // Derived from danix2-hugo-theme/assets/js/matrix-rain.js (function() { if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return; var header = document.querySelector('.site-header'); if (!header) return; // Canvas var canvas = document.createElement('canvas'); canvas.id = 'matrix-rain'; canvas.style.cssText = 'position:absolute;top:0;right:0;height:100%;pointer-events:none;z-index:0;'; header.appendChild(canvas); // Gradient fade div — covers left portion of canvas area, left→transparent var fade = document.createElement('div'); fade.style.cssText = 'position:absolute;top:0;right:0;bottom:0;width:65%;background:linear-gradient(to right,var(--bg-card) 0%,transparent 60%);pointer-events:none;z-index:1;'; header.appendChild(fade); var ctx = canvas.getContext('2d'); var columns = []; var frameCount = 0; var colors = { accent: '#5c9cf5', accent2: '#4ec97b', bg: '#161b25', head: '#ffffff' }; var ASCII = Array.from({ length: 94 }, function(_, i) { return String.fromCharCode(33 + i); }); var KATA = Array.from({ length: 96 }, function(_, i) { return String.fromCodePoint(0x30a0 + i); }); var CHARS = shuffle([].concat(ASCII, KATA, KATA, KATA)); function shuffle(arr) { for (var i = arr.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } return arr; } function hexToRgba(color, alpha) { var rgbMatch = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/); if (rgbMatch) return 'rgba(' + rgbMatch[1] + ',' + rgbMatch[2] + ',' + rgbMatch[3] + ',' + alpha + ')'; var hex = color.replace('#', ''); var r = parseInt(hex.substring(0, 2), 16); var g = parseInt(hex.substring(2, 4), 16); var b = parseInt(hex.substring(4, 6), 16); return 'rgba(' + r + ',' + g + ',' + b + ',' + alpha + ')'; } function sampleColors() { var style = getComputedStyle(document.documentElement); var isDark = !document.documentElement.classList.contains('theme-light'); colors.accent = style.getPropertyValue('--accent').trim() || '#5c9cf5'; colors.accent2 = style.getPropertyValue('--accent2').trim() || '#4ec97b'; colors.bg = style.getPropertyValue('--bg-card').trim() || '#161b25'; colors.head = isDark ? '#ffffff' : '#1a0533'; } function resizeCanvas() { canvas.width = Math.floor(header.offsetWidth * 0.65); canvas.height = header.offsetHeight; ctx.font = '14px "IBM Plex Mono", monospace'; ctx.textBaseline = 'top'; initColumns(); } function initColumns() { columns = []; var columnWidth = 14; var columnCount = Math.floor(canvas.width / columnWidth); for (var i = 0; i < columnCount; i++) { columns.push({ x: i * columnWidth, y: -Math.floor(Math.random() * 40), speed: 2 + Math.floor(Math.random() * 3), color: Math.random() < 0.6 ? 'accent2' : 'accent', charIndex: Math.floor(Math.random() * CHARS.length), length: 8 + Math.floor(Math.random() * 13) }); } } function setupThemeObserver() { var observer = new MutationObserver(function(mutations) { for (var i = 0; i < mutations.length; i++) { if (mutations[i].attributeName === 'class') { sampleColors(); break; } } }); observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] }); } function drawFrame() { frameCount++; ctx.fillStyle = hexToRgba(colors.bg, 0.085); ctx.fillRect(0, 0, canvas.width, canvas.height); for (var ci = 0; ci < columns.length; ci++) { var col = columns[ci]; if (frameCount % col.speed !== 0) continue; ctx.fillStyle = colors[col.color]; for (var i = 1; i <= col.length; i++) { var trailY = (col.y - i) * 14; if (trailY < 0) continue; var trailCharIndex = (col.charIndex - i + CHARS.length) % CHARS.length; ctx.fillText(CHARS[trailCharIndex], col.x, trailY); } ctx.fillStyle = colors.head; ctx.fillText(CHARS[col.charIndex % CHARS.length], col.x, col.y * 14); col.y++; col.charIndex = (col.charIndex + 1) % CHARS.length; if (col.y * 14 > canvas.height + col.length * 14) { col.y = -Math.floor(Math.random() * 20); col.charIndex = Math.floor(Math.random() * CHARS.length); col.color = Math.random() < 0.6 ? 'accent2' : 'accent'; } } requestAnimationFrame(drawFrame); } sampleColors(); resizeCanvas(); setupThemeObserver(); var resizeTimer; window.addEventListener('resize', function() { clearTimeout(resizeTimer); resizeTimer = setTimeout(resizeCanvas, 150); }); document.fonts.ready.then(function() { requestAnimationFrame(drawFrame); }); })();