summaryrefslogtreecommitdiffstats
path: root/head.html
diff options
context:
space:
mode:
Diffstat (limited to 'head.html')
-rw-r--r--head.html97
1 files changed, 97 insertions, 0 deletions
diff --git a/head.html b/head.html
index 68bda81..56af0ea 100644
--- a/head.html
+++ b/head.html
@@ -32,3 +32,100 @@ document.addEventListener('DOMContentLoaded', function() {
}
});
</script>
+<script>
+(function() {
+ if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
+
+ var CHARS = 'ヲイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~0123456789';
+ var FONT_SIZE = 14;
+ var TRAIL_LEN = 12;
+ var TRAIL_FADE = 0.09;
+ var GREEN_MAX = 0.55;
+ var PURPLE_MAX = 0.75;
+ var PURPLE_PROB = 0.35;
+ var SPEED_MIN = 0.3;
+ var SPEED_MAX = 0.8;
+ var CANVAS_W_RATIO = 0.65;
+ var BG_CLEAR = 'rgba(12,21,32,0.18)';
+
+ function randChar() {
+ return CHARS[Math.floor(Math.random() * CHARS.length)];
+ }
+
+ function makeColumn() {
+ return {
+ y: Math.random() * -20,
+ speed: SPEED_MIN + Math.random() * (SPEED_MAX - SPEED_MIN),
+ purple: Math.random() < PURPLE_PROB,
+ chars: Array.from({length: TRAIL_LEN}, randChar)
+ };
+ }
+
+ function init(canvas, header) {
+ canvas.width = Math.floor(header.offsetWidth * CANVAS_W_RATIO);
+ canvas.height = header.offsetHeight;
+ var cols = Math.floor(canvas.width / FONT_SIZE);
+ return Array.from({length: cols}, makeColumn);
+ }
+
+ function draw(ctx, canvas, drops) {
+ ctx.fillStyle = BG_CLEAR;
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ ctx.font = FONT_SIZE + 'px "JetBrains Mono", monospace';
+
+ var rows = Math.ceil(canvas.height / FONT_SIZE);
+ drops.forEach(function(drop, i) {
+ var x = i * FONT_SIZE;
+ for (var r = 0; r < TRAIL_LEN; r++) {
+ var row = Math.floor(drop.y) - r;
+ if (row < 0 || row >= rows) continue;
+ var fade = Math.max(0, 1 - r * TRAIL_FADE);
+ if (r === 0 && drop.purple) {
+ ctx.fillStyle = 'rgba(168,85,247,' + (PURPLE_MAX * fade).toFixed(2) + ')';
+ } else {
+ ctx.fillStyle = 'rgba(0,255,136,' + (GREEN_MAX * fade).toFixed(2) + ')';
+ }
+ if (Math.random() < 0.03) drop.chars[r % TRAIL_LEN] = randChar();
+ ctx.fillText(drop.chars[r % TRAIL_LEN], x, row * FONT_SIZE);
+ }
+ drop.y += drop.speed;
+ if (drop.y * FONT_SIZE > canvas.height + 200) {
+ drop.y = Math.random() * -10;
+ drop.speed = SPEED_MIN + Math.random() * (SPEED_MAX - SPEED_MIN);
+ drop.purple = Math.random() < PURPLE_PROB;
+ }
+ });
+ }
+
+ document.addEventListener('DOMContentLoaded', function() {
+ var header = document.querySelector('table#header');
+ if (!header) return;
+
+ var canvas = document.createElement('canvas');
+ canvas.id = 'matrix-rain-canvas';
+ canvas.style.cssText = 'position:absolute;top:0;right:0;height:100%;pointer-events:none;z-index:0;';
+ header.appendChild(canvas);
+
+ var fade = document.createElement('div');
+ fade.id = 'matrix-rain-fade';
+ fade.style.cssText = 'position:absolute;top:0;left:0;bottom:0;width:75%;background:linear-gradient(to right,rgba(12,21,32,0.88) 40%,transparent 100%);pointer-events:none;z-index:1;';
+ header.appendChild(fade);
+
+ var ctx = canvas.getContext('2d');
+ var drops = init(canvas, header);
+
+ var raf;
+ function loop() {
+ draw(ctx, canvas, drops);
+ raf = requestAnimationFrame(loop);
+ }
+ loop();
+
+ window.addEventListener('resize', function() {
+ cancelAnimationFrame(raf);
+ drops = init(canvas, header);
+ loop();
+ });
+ });
+})();
+</script>