// Site-wide animated code background — едва заметный, ненавязчивый // Рендерим на canvas, fixed behind all content function CodeRainBg(){ const canvasRef = React.useRef(null); React.useEffect(()=>{ const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext('2d'); let w = 0, h = 0, dpr = Math.min(window.devicePixelRatio || 1, 2); let columns = []; let dollars = []; const fontSize = 14; const dollarSize = 18; // Символы: смесь кода, латиницы, цифр, знаков const chars = 'const let await async {} [] <> => 01 + = * $ # @ ! ? vibe agent ship'.split(' '); function resize(){ w = window.innerWidth; h = window.innerHeight; canvas.width = w*dpr; canvas.height = h*dpr; canvas.style.width = w+'px'; canvas.style.height = h+'px'; ctx.scale(dpr, dpr); const colWidth = 28; const cols = Math.ceil(w / colWidth); columns = Array.from({length: cols}, (_,i)=>({ x: i*colWidth + 6, y: Math.random() * h, speed: 0.25 + Math.random()*0.6, len: 6 + Math.floor(Math.random()*14), chars: Array.from({length: 20}, ()=>chars[Math.floor(Math.random()*chars.length)]), tick: 0, })); // Sparse dollar signs — зелёные, плывут медленнее, далеко друг от друга const dollarCount = Math.max(8, Math.floor((w*h)/38000)); dollars = Array.from({length: dollarCount}, ()=>({ x: Math.random()*w, y: Math.random()*h, speed: 0.08 + Math.random()*0.22, drift: (Math.random()-0.5)*0.15, size: dollarSize + Math.random()*10, alpha: 0.018 + Math.random()*0.025, })); } resize(); window.addEventListener('resize', resize); ctx.font = `${fontSize}px "JetBrains Mono", ui-monospace, monospace`; ctx.textBaseline = 'top'; let rafId; function draw(){ // Слабое «стирание» — оставляет хвост ctx.fillStyle = 'rgba(10, 10, 11, 0.12)'; ctx.fillRect(0, 0, w, h); for (const c of columns){ c.tick += 1; if (c.tick % 8 === 0){ // иногда подменяем символ c.chars[0] = chars[Math.floor(Math.random()*chars.length)]; } // Head — чуть ярче ctx.fillStyle = 'rgba(124, 92, 255, 0.07)'; ctx.fillText(c.chars[0], c.x, c.y); // Tail — совсем тусклый ctx.fillStyle = 'rgba(124, 92, 255, 0.018)'; for (let i=1;i h + c.len * (fontSize + 4)){ c.y = -10; c.speed = 0.25 + Math.random()*0.6; c.len = 6 + Math.floor(Math.random()*14); } } // Зелёные знаки $ — очень тусклые, плывут вверх for (const d of dollars){ ctx.font = `700 ${d.size}px "JetBrains Mono", ui-monospace, monospace`; ctx.fillStyle = `rgba(62, 224, 138, ${d.alpha})`; ctx.fillText('$', d.x, d.y); d.y -= d.speed; d.x += d.drift; if (d.y < -d.size){ d.y = h + d.size; d.x = Math.random()*w; d.alpha = 0.018 + Math.random()*0.025; d.size = dollarSize + Math.random()*10; } } // Возвращаем шрифт для следующего кадра кода ctx.font = `${fontSize}px "JetBrains Mono", ui-monospace, monospace`; rafId = requestAnimationFrame(draw); } draw(); // Пауза, если вкладка не активна — экономим батарею const onVis = ()=>{ if (document.hidden && rafId) { cancelAnimationFrame(rafId); rafId = null; } else if (!rafId) { draw(); } }; document.addEventListener('visibilitychange', onVis); return ()=>{ cancelAnimationFrame(rafId); window.removeEventListener('resize', resize); document.removeEventListener('visibilitychange', onVis); }; }, []); return ( <> {/* Верхний виньет — чтобы текст читался */}
); } window.CodeRainBg = CodeRainBg;