285 lines
12 KiB
HTML
285 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>16 - MATRIX RAIN | 赛博代码雨</title>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body { background: #000; overflow: hidden; font-family: 'Courier New', monospace; }
|
|
#canvas-container { width: 100vw; height: 100vh; position: relative; }
|
|
#matrix-canvas {
|
|
position: absolute; top: 0; left: 0;
|
|
width: 100%; height: 100%;
|
|
}
|
|
.info {
|
|
position: fixed; top: 20px; left: 20px;
|
|
color: #0f0; font-size: 14px;
|
|
text-shadow: 0 0 10px #0f0;
|
|
z-index: 100; pointer-events: none;
|
|
background: rgba(0,20,0,0.8);
|
|
padding: 15px; border-radius: 8px;
|
|
border: 1px solid #0f0;
|
|
}
|
|
.info h1 { font-size: 20px; margin-bottom: 8px; }
|
|
.info p { opacity: 0.8; font-size: 12px; margin: 4px 0; }
|
|
.controls {
|
|
position: fixed; bottom: 20px; left: 50%;
|
|
transform: translateX(-50%);
|
|
display: flex; gap: 15px;
|
|
background: rgba(0,20,0,0.9);
|
|
padding: 15px 25px;
|
|
border-radius: 30px;
|
|
border: 1px solid #0f0;
|
|
z-index: 100;
|
|
}
|
|
.btn {
|
|
background: transparent;
|
|
border: 1px solid #0f0;
|
|
color: #0f0;
|
|
padding: 8px 16px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-family: inherit;
|
|
font-size: 12px;
|
|
transition: all 0.3s;
|
|
text-shadow: 0 0 5px #0f0;
|
|
}
|
|
.btn:hover, .btn.active {
|
|
background: #0f0;
|
|
color: #000;
|
|
box-shadow: 0 0 20px #0f0;
|
|
}
|
|
.message {
|
|
position: fixed; top: 50%; left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
font-size: 60px; color: #0f0;
|
|
text-shadow: 0 0 30px #0f0;
|
|
opacity: 0; pointer-events: none;
|
|
transition: opacity 0.5s;
|
|
font-family: 'STKaiti', 'KaiTi', monospace;
|
|
text-align: center;
|
|
}
|
|
.message.show { opacity: 1; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="canvas-container">
|
|
<canvas id="matrix-canvas"></canvas>
|
|
</div>
|
|
<div class="info">
|
|
<h1>16 - MATRIX RAIN</h1>
|
|
<p>赛博朋克代码雨 - 中文黑客帝国</p>
|
|
<p>字符流: <span id="streams">0</span> | FPS: <span id="fps">0</span></p>
|
|
</div>
|
|
<div class="controls">
|
|
<button class="btn active" onclick="setMode('mix')" id="btn-mix">混合</button>
|
|
<button class="btn" onclick="setMode('code')" id="btn-code">代码</button>
|
|
<button class="btn" onclick="setMode('cn')" id="btn-cn">汉字</button>
|
|
<button class="btn" onclick="setMode('poem')" id="btn-poem">诗词</button>
|
|
<button class="btn" onclick="triggerGlitch()">故障</button>
|
|
</div>
|
|
<div class="message" id="message">Wake up, Neo...</div>
|
|
|
|
<script>
|
|
const canvas = document.getElementById('matrix-canvas');
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
// 字符集
|
|
const CHAR_SETS = {
|
|
code: '0123456789ABCDEF<>/{}[];=+-*&|%$#@!?~',
|
|
cn: '的一是在不了有和人这中大为上个国我以要他时来用们生到作地于出就分对成会可主发年动同工也能下过子说产种面而方后多定行学法所民得经十三之进着等部度家电力里如水化高自二理起小物现实加量都两体制机当使点从业本去把性好应开它合还因由其些然前外天政四日那社义事平形相全表间样与关各重新线内数正心反你明看原又么利比或但质气第向道命此变条只没结解问意建月公无系军很情者最立代想已通并提直题党程展五果料象员革位入常文总次品式活设及管特件长求老头基资边流路级少图山统接知较将组见计别她手角期根论运农指几九区强放决西被干做必战先回则任取完举色',
|
|
poem: '春江花月夜人生若只如初见何事秋风悲画扇曾经沧海难为水除却巫山不是云玲珑骰子安红豆入骨相思知不知两情若是久长时又岂在朝朝暮暮身无彩凤双飞翼心有灵犀一点通',
|
|
mix: null // 混合使用
|
|
};
|
|
|
|
// 编程代码片段
|
|
const CODE_SNIPPETS = [
|
|
'import numpy', 'const x = 0', 'function(){}', 'class Matrix', 'async await',
|
|
'docker run', 'git commit', 'npm install', 'sudo rm -rf', 'hello world',
|
|
'python3 -m', 'pip install', 'apt update', 'yarn build', 'docker-compose',
|
|
'kubectl get', 'terraform', 'ansible-play', 'jenkins build', 'ci/cd pipe',
|
|
'bitcoin hash', 'blockchain', 'smart contract', 'defi protocol', 'nft mint',
|
|
'neural net', 'deep learning', 'gradient descent', 'backpropagation', 'ai model'
|
|
];
|
|
|
|
let mode = 'mix';
|
|
let fontSize = 14;
|
|
let columns, drops, speeds, lengths, colors;
|
|
let particles = [];
|
|
let glitchMode = false;
|
|
|
|
function resize() {
|
|
canvas.width = window.innerWidth;
|
|
canvas.height = window.innerHeight;
|
|
initMatrix();
|
|
}
|
|
|
|
function initMatrix() {
|
|
columns = Math.floor(canvas.width / fontSize);
|
|
drops = [];
|
|
speeds = [];
|
|
lengths = [];
|
|
colors = [];
|
|
|
|
for (let i = 0; i < columns; i++) {
|
|
drops[i] = Math.random() * -100; // 随机起始高度
|
|
speeds[i] = 0.5 + Math.random() * 1.5;
|
|
lengths[i] = 10 + Math.floor(Math.random() * 20);
|
|
colors[i] = Math.random();
|
|
}
|
|
}
|
|
|
|
function getChar() {
|
|
const set = mode === 'mix' ? (Math.random() > 0.5 ? CHAR_SETS.code : CHAR_SETS.cn) : CHAR_SETS[mode];
|
|
if (mode === 'poem') return CHAR_SETS.poem[Math.floor(Math.random() * CHAR_SETS.poem.length)];
|
|
if (Math.random() < 0.1) return CODE_SNIPPETS[Math.floor(Math.random() * CODE_SNIPPETS.length)][0];
|
|
return set[Math.floor(Math.random() * set.length)];
|
|
}
|
|
|
|
function getColor(alpha, isHead) {
|
|
if (glitchMode) {
|
|
const glitch = Math.random();
|
|
if (glitch < 0.33) return `rgba(255,0,0,${alpha})`;
|
|
if (glitch < 0.66) return `rgba(0,255,255,${alpha})`;
|
|
return `rgba(255,255,0,${alpha})`;
|
|
}
|
|
|
|
if (isHead) {
|
|
return `rgba(255,255,255,${alpha})`; // 头部白色
|
|
}
|
|
|
|
// 绿色渐变
|
|
const green = 200 + Math.floor(colors[i % columns] * 55);
|
|
return `rgba(0,${green},0,${alpha})`;
|
|
}
|
|
|
|
// 粒子效果(用于故障时)
|
|
class Particle {
|
|
constructor() {
|
|
this.x = Math.random() * canvas.width;
|
|
this.y = Math.random() * canvas.height;
|
|
this.vx = (Math.random() - 0.5) * 10;
|
|
this.vy = (Math.random() - 0.5) * 10;
|
|
this.life = 1;
|
|
this.color = Math.random() > 0.5 ? '#f00' : '#0ff';
|
|
}
|
|
update() {
|
|
this.x += this.vx;
|
|
this.y += this.vy;
|
|
this.life -= 0.02;
|
|
}
|
|
draw() {
|
|
ctx.fillStyle = this.color;
|
|
ctx.globalAlpha = this.life;
|
|
ctx.fillRect(this.x, this.y, 3, 3);
|
|
ctx.globalAlpha = 1;
|
|
}
|
|
}
|
|
|
|
function triggerGlitch() {
|
|
glitchMode = true;
|
|
|
|
// 显示消息
|
|
const msg = document.getElementById('message');
|
|
const messages = ['Wake up, Neo...', 'The Matrix has you...', 'Follow the white rabbit.', 'Knock, knock.'];
|
|
msg.textContent = messages[Math.floor(Math.random() * messages.length)];
|
|
msg.classList.add('show');
|
|
|
|
// 添加粒子
|
|
for (let i = 0; i < 50; i++) {
|
|
particles.push(new Particle());
|
|
}
|
|
|
|
setTimeout(() => {
|
|
glitchMode = false;
|
|
msg.classList.remove('show');
|
|
}, 2000);
|
|
}
|
|
|
|
function setMode(newMode) {
|
|
mode = newMode;
|
|
document.querySelectorAll('.btn').forEach(b => b.classList.remove('active'));
|
|
document.getElementById('btn-' + newMode).classList.add('active');
|
|
}
|
|
|
|
let lastTime = performance.now(), frameCount = 0;
|
|
const fpsEl = document.getElementById('fps');
|
|
const streamsEl = document.getElementById('streams');
|
|
|
|
function draw() {
|
|
// 半透明黑色遮罩实现拖尾效果
|
|
ctx.fillStyle = glitchMode ? 'rgba(20,0,0,0.1)' : 'rgba(0,0,0,0.05)';
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
|
ctx.font = fontSize + 'px monospace';
|
|
|
|
for (let i = 0; i < columns; i++) {
|
|
const x = i * fontSize;
|
|
const y = drops[i] * fontSize;
|
|
|
|
// 绘制尾部
|
|
for (let j = 1; j < lengths[i]; j++) {
|
|
if (drops[i] - j >= 0) {
|
|
const tailY = (drops[i] - j) * fontSize;
|
|
const alpha = 1 - j / lengths[i];
|
|
ctx.fillStyle = glitchMode ?
|
|
(Math.random() > 0.5 ? `rgba(255,0,0,${alpha})` : `rgba(0,255,255,${alpha})`) :
|
|
`rgba(0,${150 + j * 5},0,${alpha})`;
|
|
|
|
if (Math.random() > 0.1) { // 随机闪烁
|
|
const char = getChar();
|
|
ctx.fillText(char, x, tailY);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 绘制头部(白色高亮)
|
|
ctx.fillStyle = glitchMode ? '#fff' : '#fff';
|
|
const headChar = getChar();
|
|
ctx.fillText(headChar, x, y);
|
|
|
|
// 随机重置
|
|
if (y > canvas.height && Math.random() > 0.975) {
|
|
drops[i] = 0;
|
|
lengths[i] = 10 + Math.floor(Math.random() * 20);
|
|
speeds[i] = 0.5 + Math.random() * 1.5;
|
|
}
|
|
|
|
drops[i] += speeds[i] * 0.5;
|
|
}
|
|
|
|
// 绘制故障粒子
|
|
particles = particles.filter(p => {
|
|
p.update();
|
|
p.draw();
|
|
return p.life > 0;
|
|
});
|
|
|
|
// 偶尔的水平故障线
|
|
if (glitchMode && Math.random() > 0.7) {
|
|
const glitchY = Math.random() * canvas.height;
|
|
const glitchHeight = Math.random() * 50 + 10;
|
|
const imageData = ctx.getImageData(0, glitchY, canvas.width, glitchHeight);
|
|
ctx.putImageData(imageData, (Math.random() - 0.5) * 50, glitchY);
|
|
}
|
|
|
|
frameCount++;
|
|
const now = performance.now();
|
|
if (now - lastTime >= 1000) {
|
|
fpsEl.textContent = frameCount;
|
|
streamsEl.textContent = columns;
|
|
frameCount = 0;
|
|
lastTime = now;
|
|
}
|
|
|
|
requestAnimationFrame(draw);
|
|
}
|
|
|
|
window.addEventListener('resize', resize);
|
|
resize();
|
|
draw();
|
|
</script>
|
|
</body>
|
|
</html>
|