import { useEffect, useRef } from 'react'; const GLYPHS = 'アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホ0123456789:.=*+-<>¦JAVIDSALIMOV'; export function MatrixRain({ opacity = 0.4 }: { opacity?: number }) { const ref = useRef(null); useEffect(() => { const canvas = ref.current; if (!canvas) return; const ctx = canvas.getContext('2d'); if (!ctx) return; let raf = 0; let cols = 0; let drops: number[] = []; const fontSize = 16; const resize = () => { canvas.width = canvas.offsetWidth * devicePixelRatio; canvas.height = canvas.offsetHeight * devicePixelRatio; ctx.scale(devicePixelRatio, devicePixelRatio); cols = Math.floor(canvas.offsetWidth / fontSize); drops = Array.from({ length: cols }, () => Math.floor((Math.random() * canvas.offsetHeight) / fontSize), ); }; resize(); let frame = 0; const draw = () => { frame++; // throttle to ~20fps for that staggered glyph cadence if (frame % 3 === 0) { ctx.fillStyle = 'rgba(4, 7, 10, 0.08)'; ctx.fillRect(0, 0, canvas.offsetWidth, canvas.offsetHeight); ctx.font = `${fontSize}px 'Share Tech Mono', monospace`; for (let i = 0; i < drops.length; i++) { const char = GLYPHS[Math.floor(Math.random() * GLYPHS.length)]; const x = i * fontSize; const y = drops[i] * fontSize; // lead glyph bright, trail dim ctx.fillStyle = Math.random() > 0.975 ? '#d5ffe4' : '#1fd65f'; ctx.fillText(char, x, y); if (y > canvas.offsetHeight && Math.random() > 0.975) drops[i] = 0; drops[i]++; } } raf = requestAnimationFrame(draw); }; draw(); window.addEventListener('resize', resize); return () => { cancelAnimationFrame(raf); window.removeEventListener('resize', resize); }; }, []); return ( ); }