diff options
| author | Danilo M. <danix@danix.xyz> | 2026-05-11 14:16:52 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-05-11 14:16:52 +0200 |
| commit | 8e7bb77a16cad59928225c59b2e6f43fd00d1cb4 (patch) | |
| tree | 2a17d920482deb9f15b91a8524ace31228cf0b47 /.assets/js/matrix-rain.js | |
| parent | 181538b5204bd5f14d5f4a5fc23c50e7dc3db75d (diff) | |
| download | pkgs-html-structure-8e7bb77a16cad59928225c59b2e6f43fd00d1cb4.tar.gz pkgs-html-structure-8e7bb77a16cad59928225c59b2e6f43fd00d1cb4.zip | |
Move matrix-rain.js to .assets/js/, add favicon.png to .assets/img/.
Update all header templates with new JS path and favicon link tag.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to '.assets/js/matrix-rain.js')
| -rw-r--r-- | .assets/js/matrix-rain.js | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/.assets/js/matrix-rain.js b/.assets/js/matrix-rain.js new file mode 100644 index 0000000..e22ada2 --- /dev/null +++ b/.assets/js/matrix-rain.js @@ -0,0 +1,136 @@ +// 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); + }); +})(); |
