Spaces:
Sleeping
Sleeping
| import React, { useEffect, useRef } from 'react'; | |
| const Background: React.FC<{ theme: 'light' | 'dark' }> = ({ theme }) => { | |
| const canvasRef = useRef<HTMLCanvasElement>(null); | |
| useEffect(() => { | |
| const canvas = canvasRef.current; | |
| if (!canvas) return; | |
| const ctx = canvas.getContext('2d'); | |
| if (!ctx) return; | |
| let animationFrameId: number; | |
| let time = 0; | |
| const resize = () => { | |
| canvas.width = window.innerWidth; | |
| canvas.height = window.innerHeight; | |
| }; | |
| window.addEventListener('resize', resize); | |
| resize(); | |
| const points: { x: number; y: number; z: number }[] = []; | |
| const numPoints = 50; // Reduced count slightly | |
| const size = 300; | |
| // Create a cube of dots | |
| for (let i = 0; i < numPoints; i++) { | |
| points.push({ | |
| x: (Math.random() - 0.5) * size, | |
| y: (Math.random() - 0.5) * size, | |
| z: (Math.random() - 0.5) * size, | |
| }); | |
| } | |
| const draw = () => { | |
| // Varied, slower speed | |
| time += 0.001 + Math.sin(Date.now() * 0.0005) * 0.0005; | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| const cx = canvas.width / 2; | |
| const cy = canvas.height / 2; | |
| // Much lower opacity for better contrast with content | |
| const color = theme === 'dark' ? 'rgba(255, 255, 255, 0.08)' : 'rgba(0, 0, 0, 0.05)'; | |
| const connectionColor = theme === 'dark' ? 'rgba(255, 255, 255, 0.02)' : 'rgba(0, 0, 0, 0.02)'; | |
| ctx.fillStyle = color; | |
| ctx.strokeStyle = connectionColor; | |
| // Rotate points | |
| const rotatedPoints = points.map(p => { | |
| // Rotate Y | |
| let x = p.x * Math.cos(time) - p.z * Math.sin(time); | |
| let z = p.x * Math.sin(time) + p.z * Math.cos(time); | |
| // Rotate X | |
| let y = p.y * Math.cos(time * 0.5) - z * Math.sin(time * 0.5); | |
| z = p.y * Math.sin(time * 0.5) + z * Math.cos(time * 0.5); | |
| // Perspective projection | |
| const scale = 800 / (800 + z); | |
| return { | |
| x: cx + x * scale, | |
| y: cy + y * scale, | |
| scale | |
| }; | |
| }); | |
| // Draw connections | |
| ctx.beginPath(); | |
| for (let i = 0; i < rotatedPoints.length; i++) { | |
| for (let j = i + 1; j < rotatedPoints.length; j++) { | |
| const dx = rotatedPoints[i].x - rotatedPoints[j].x; | |
| const dy = rotatedPoints[i].y - rotatedPoints[j].y; | |
| const dist = Math.sqrt(dx * dx + dy * dy); | |
| if (dist < 120) { | |
| ctx.moveTo(rotatedPoints[i].x, rotatedPoints[i].y); | |
| ctx.lineTo(rotatedPoints[j].x, rotatedPoints[j].y); | |
| } | |
| } | |
| } | |
| ctx.stroke(); | |
| // Draw points | |
| rotatedPoints.forEach(p => { | |
| ctx.beginPath(); | |
| ctx.arc(p.x, p.y, 2 * p.scale, 0, Math.PI * 2); | |
| ctx.fill(); | |
| }); | |
| animationFrameId = requestAnimationFrame(draw); | |
| }; | |
| draw(); | |
| return () => { | |
| window.removeEventListener('resize', resize); | |
| cancelAnimationFrame(animationFrameId); | |
| }; | |
| }, [theme]); | |
| return ( | |
| <canvas | |
| ref={canvasRef} | |
| className="fixed top-0 left-0 w-full h-full -z-10 pointer-events-none transition-opacity duration-1000 opacity-20" | |
| /> | |
| ); | |
| }; | |
| export default Background; | |