dashboard(home): matrix-rain backdrop on the H0M3 page
Adds the falling-glyph "digital rain" effect the operator asked for as a dimmed background on the home hub. - index.html: a decorative full-viewport <canvas id="matrix-rain"> (aria-hidden, pointer-events:none) as the first body child. - home.css: fixes it behind the content (z-index:-1, inset:0) at low opacity (0.16) so the menu tiles stay legible — the "dimmed bg". - home.js: startMatrixRain() drives the classic stepped digital rain. Glyph + fade-trail colours are RESOLVED FROM THE STYLIX PALETTE at runtime (--green glyphs, --bg trail; resolved via a probe element since getComputedStyle returns custom props unresolved), so a theme swap re-colours it rather than hardcoding matrix-green. Stepped via a 55ms interval, paused while the tab is hidden, and skipped entirely under prefers-reduced-motion. CSS/JS only, home page only.
This commit is contained in:
parent
58cc720173
commit
31001d52e8
3 changed files with 92 additions and 0 deletions
|
|
@ -13,6 +13,21 @@ body.home-shell {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Matrix-rain backdrop (driven by home.js). Fixed, full-viewport, dimmed,
|
||||||
|
and behind every other element (z-index below the auto-stacked content).
|
||||||
|
Decorative only — pointer-events off so it never intercepts clicks. The
|
||||||
|
low opacity keeps the menu tiles legible over it; glyph + fade colours
|
||||||
|
are themed from the stylix palette in JS. */
|
||||||
|
#matrix-rain {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
opacity: 0.16;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.home-header {
|
.home-header {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 1.5em 0 0.5em;
|
padding: 1.5em 0 0.5em;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
// 2. fill the swarm/hive identity line when configured;
|
// 2. fill the swarm/hive identity line when configured;
|
||||||
// 3. render the shared server-warnings banner (top of every page).
|
// 3. render the shared server-warnings banner (top of every page).
|
||||||
// No SSE — a portal doesn't need live updates.
|
// No SSE — a portal doesn't need live updates.
|
||||||
|
//
|
||||||
|
// It also drives the decorative matrix-rain backdrop (#matrix-rain) —
|
||||||
|
// see startMatrixRain() at the foot.
|
||||||
|
|
||||||
import { renderServerWarnings } from './common.js';
|
import { renderServerWarnings } from './common.js';
|
||||||
|
|
||||||
|
|
@ -48,3 +51,70 @@ async function init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
init();
|
init();
|
||||||
|
|
||||||
|
// ── matrix-rain backdrop ─────────────────────────────────────────────
|
||||||
|
// Classic falling-glyph "digital rain" on the #matrix-rain canvas, purely
|
||||||
|
// decorative. Colours are resolved from the live stylix palette (--green
|
||||||
|
// glyphs, --bg fade-trail) so a theme swap re-colours it; the CSS already
|
||||||
|
// dims it + parks it behind the content. Stepped via setInterval (the
|
||||||
|
// steppy cadence is the look and costs little CPU); paused while the tab is
|
||||||
|
// hidden, and skipped entirely under prefers-reduced-motion.
|
||||||
|
function startMatrixRain() {
|
||||||
|
const canvas = $('matrix-rain');
|
||||||
|
if (!canvas || !canvas.getContext) return;
|
||||||
|
if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// getComputedStyle on a custom property returns its *declared* value
|
||||||
|
// (e.g. "var(--base0B)") unresolved, so resolve to a concrete rgb() by
|
||||||
|
// probing a real `color` computation instead.
|
||||||
|
function resolveColor(varName, fallback) {
|
||||||
|
const probe = document.createElement('span');
|
||||||
|
probe.style.color = `var(${varName})`;
|
||||||
|
probe.style.display = 'none';
|
||||||
|
document.body.appendChild(probe);
|
||||||
|
const c = getComputedStyle(probe).color;
|
||||||
|
probe.remove();
|
||||||
|
return /^rgb/.test(c) ? c : fallback;
|
||||||
|
}
|
||||||
|
const glyphColor = resolveColor('--green', 'rgb(166, 227, 161)');
|
||||||
|
const fadeColor = resolveColor('--bg', 'rgb(30, 30, 46)')
|
||||||
|
.replace(/^rgb\(/, 'rgba(').replace(/\)$/, ', 0.09)');
|
||||||
|
|
||||||
|
const GLYPHS = 'アイウエオカキクケコサシスセソタチツテト0123456789:=*+-<>¦';
|
||||||
|
const CELL = 16; // glyph cell size (px)
|
||||||
|
let cols = 0;
|
||||||
|
let drops = [];
|
||||||
|
|
||||||
|
function resize() {
|
||||||
|
canvas.width = canvas.offsetWidth;
|
||||||
|
canvas.height = canvas.offsetHeight;
|
||||||
|
cols = Math.max(1, Math.ceil(canvas.width / CELL));
|
||||||
|
drops = Array.from({ length: cols }, () => Math.floor(Math.random() * -40));
|
||||||
|
ctx.font = `${CELL}px monospace`; // canvas reset on resize clears this
|
||||||
|
}
|
||||||
|
|
||||||
|
function tick() {
|
||||||
|
// Translucent bg wash leaves a fading trail behind each glyph.
|
||||||
|
ctx.fillStyle = fadeColor;
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
ctx.fillStyle = glyphColor;
|
||||||
|
for (let i = 0; i < cols; i++) {
|
||||||
|
const ch = GLYPHS[(Math.random() * GLYPHS.length) | 0];
|
||||||
|
ctx.fillText(ch, i * CELL, drops[i] * CELL);
|
||||||
|
if (drops[i] * CELL > canvas.height && Math.random() > 0.975) drops[i] = 0;
|
||||||
|
drops[i]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let timer = null;
|
||||||
|
const play = () => { if (!timer) timer = setInterval(tick, 55); };
|
||||||
|
const pause = () => { if (timer) { clearInterval(timer); timer = null; } };
|
||||||
|
|
||||||
|
resize();
|
||||||
|
window.addEventListener('resize', resize);
|
||||||
|
document.addEventListener('visibilitychange', () => (document.hidden ? pause() : play()));
|
||||||
|
play();
|
||||||
|
}
|
||||||
|
|
||||||
|
startMatrixRain();
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,13 @@
|
||||||
</head>
|
</head>
|
||||||
<body class="home-shell">
|
<body class="home-shell">
|
||||||
|
|
||||||
|
<!-- Decorative matrix-rain backdrop: a dimmed full-viewport canvas of
|
||||||
|
falling glyphs behind everything (home.js drives it). aria-hidden +
|
||||||
|
pointer-events:none so it's purely cosmetic; glyph/bg colours are
|
||||||
|
read from the stylix palette at runtime so a theme swap re-colours
|
||||||
|
it, and it's disabled under prefers-reduced-motion. -->
|
||||||
|
<canvas id="matrix-rain" aria-hidden="true"></canvas>
|
||||||
|
|
||||||
<!-- H0M3: the menu hub. A plain grid of links to every top-level
|
<!-- H0M3: the menu hub. A plain grid of links to every top-level
|
||||||
surface. This is the page served at `/` — the landing page — with
|
surface. This is the page served at `/` — the landing page — with
|
||||||
the dashboard relocated to /dashboard.html. No tabbar / SSE —
|
the dashboard relocated to /dashboard.html. No tabbar / SSE —
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue