diff --git a/components/tech-background.tsx b/components/tech-background.tsx index b3b7ee9..b21e3bf 100644 --- a/components/tech-background.tsx +++ b/components/tech-background.tsx @@ -12,72 +12,105 @@ export function TechBackground() { const ctx = canvas.getContext("2d") if (!ctx) return - let animationFrameId: number - let particles: Particle[] = [] - const resize = () => { canvas.width = window.innerWidth canvas.height = window.innerHeight + draw() } - class Particle { - x: number - y: number - vx: number - vy: number - size: number - opacity: number + const draw = () => { + const isDark = document.documentElement.classList.contains("dark") + + // Fill with base background color + ctx.fillStyle = isDark ? "hsl(222, 47%, 6%)" : "hsl(210, 40%, 98%)" + ctx.fillRect(0, 0, canvas.width, canvas.height) - constructor(width: number, height: number) { - this.x = Math.random() * width - this.y = Math.random() * height - this.vx = (Math.random() - 0.5) * 0.3 - this.vy = (Math.random() - 0.5) * 0.3 - this.size = Math.random() * 2 + 1 - this.opacity = Math.random() * 0.5 + 0.1 + // Draw sophisticated gradient - top left corner glow + const gradient1 = ctx.createRadialGradient( + 0, + 0, + 0, + 0, + 0, + canvas.width * 0.8 + ) + + if (isDark) { + gradient1.addColorStop(0, "rgba(56, 189, 248, 0.08)") + gradient1.addColorStop(0.5, "rgba(59, 130, 246, 0.04)") + gradient1.addColorStop(1, "rgba(0, 0, 0, 0)") + } else { + gradient1.addColorStop(0, "rgba(59, 130, 246, 0.06)") + gradient1.addColorStop(0.5, "rgba(147, 197, 253, 0.04)") + gradient1.addColorStop(1, "rgba(255, 255, 255, 0)") } + + ctx.fillStyle = gradient1 + ctx.fillRect(0, 0, canvas.width, canvas.height) - update(width: number, height: number) { - this.x += this.vx - this.y += this.vy - - if (this.x < 0 || this.x > width) this.vx *= -1 - if (this.y < 0 || this.y > height) this.vy *= -1 + // Draw second gradient - bottom right corner glow + const gradient2 = ctx.createRadialGradient( + canvas.width, + canvas.height, + 0, + canvas.width, + canvas.height, + canvas.width * 0.6 + ) + + if (isDark) { + gradient2.addColorStop(0, "rgba(139, 92, 246, 0.06)") + gradient2.addColorStop(0.5, "rgba(99, 102, 241, 0.03)") + gradient2.addColorStop(1, "rgba(0, 0, 0, 0)") + } else { + gradient2.addColorStop(0, "rgba(167, 139, 250, 0.05)") + gradient2.addColorStop(0.5, "rgba(196, 181, 253, 0.03)") + gradient2.addColorStop(1, "rgba(255, 255, 255, 0)") } + + ctx.fillStyle = gradient2 + ctx.fillRect(0, 0, canvas.width, canvas.height) - draw(ctx: CanvasRenderingContext2D, isDark: boolean) { - ctx.beginPath() - ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2) - ctx.fillStyle = isDark - ? `rgba(100, 200, 255, ${this.opacity})` - : `rgba(50, 100, 150, ${this.opacity * 0.5})` - ctx.fill() - } - } - - const initParticles = () => { - particles = [] - const particleCount = Math.floor((canvas.width * canvas.height) / 15000) - for (let i = 0; i < particleCount; i++) { - particles.push(new Particle(canvas.width, canvas.height)) - } - } - - const drawGrid = (isDark: boolean) => { - const gridSize = 50 - ctx.strokeStyle = isDark - ? "rgba(100, 200, 255, 0.03)" - : "rgba(50, 100, 150, 0.05)" + // Draw subtle grid + const gridSize = 60 ctx.lineWidth = 1 - + + // Vertical lines for (let x = 0; x <= canvas.width; x += gridSize) { + const lineGradient = ctx.createLinearGradient(x, 0, x, canvas.height) + if (isDark) { + lineGradient.addColorStop(0, "rgba(148, 163, 184, 0)") + lineGradient.addColorStop(0.3, "rgba(148, 163, 184, 0.04)") + lineGradient.addColorStop(0.7, "rgba(148, 163, 184, 0.04)") + lineGradient.addColorStop(1, "rgba(148, 163, 184, 0)") + } else { + lineGradient.addColorStop(0, "rgba(71, 85, 105, 0)") + lineGradient.addColorStop(0.3, "rgba(71, 85, 105, 0.06)") + lineGradient.addColorStop(0.7, "rgba(71, 85, 105, 0.06)") + lineGradient.addColorStop(1, "rgba(71, 85, 105, 0)") + } + ctx.strokeStyle = lineGradient ctx.beginPath() ctx.moveTo(x, 0) ctx.lineTo(x, canvas.height) ctx.stroke() } + // Horizontal lines for (let y = 0; y <= canvas.height; y += gridSize) { + const lineGradient = ctx.createLinearGradient(0, y, canvas.width, y) + if (isDark) { + lineGradient.addColorStop(0, "rgba(148, 163, 184, 0)") + lineGradient.addColorStop(0.3, "rgba(148, 163, 184, 0.04)") + lineGradient.addColorStop(0.7, "rgba(148, 163, 184, 0.04)") + lineGradient.addColorStop(1, "rgba(148, 163, 184, 0)") + } else { + lineGradient.addColorStop(0, "rgba(71, 85, 105, 0)") + lineGradient.addColorStop(0.3, "rgba(71, 85, 105, 0.06)") + lineGradient.addColorStop(0.7, "rgba(71, 85, 105, 0.06)") + lineGradient.addColorStop(1, "rgba(71, 85, 105, 0)") + } + ctx.strokeStyle = lineGradient ctx.beginPath() ctx.moveTo(0, y) ctx.lineTo(canvas.width, y) @@ -85,81 +118,21 @@ export function TechBackground() { } } - const drawConnections = (isDark: boolean) => { - const maxDistance = 120 - for (let i = 0; i < particles.length; i++) { - for (let j = i + 1; j < particles.length; j++) { - const dx = particles[i].x - particles[j].x - const dy = particles[i].y - particles[j].y - const distance = Math.sqrt(dx * dx + dy * dy) - - if (distance < maxDistance) { - const opacity = (1 - distance / maxDistance) * 0.2 - ctx.beginPath() - ctx.moveTo(particles[i].x, particles[i].y) - ctx.lineTo(particles[j].x, particles[j].y) - ctx.strokeStyle = isDark - ? `rgba(100, 200, 255, ${opacity})` - : `rgba(50, 100, 150, ${opacity * 0.5})` - ctx.lineWidth = 1 - ctx.stroke() - } - } - } - } - - const animate = () => { - const isDark = document.documentElement.classList.contains("dark") - - // Fill with base background color - ctx.fillStyle = isDark ? "hsl(224, 71%, 4%)" : "hsl(0, 0%, 100%)" - ctx.fillRect(0, 0, canvas.width, canvas.height) - - // Draw subtle gradient overlay - const gradient = ctx.createRadialGradient( - canvas.width / 2, - canvas.height / 2, - 0, - canvas.width / 2, - canvas.height / 2, - canvas.width / 1.5 - ) - - if (isDark) { - gradient.addColorStop(0, "rgba(30, 60, 100, 0.1)") - gradient.addColorStop(1, "rgba(10, 20, 40, 0)") - } else { - gradient.addColorStop(0, "rgba(200, 230, 255, 0.2)") - gradient.addColorStop(1, "rgba(255, 255, 255, 0)") - } - - ctx.fillStyle = gradient - ctx.fillRect(0, 0, canvas.width, canvas.height) - - drawGrid(isDark) - - // Update and draw particles - for (const particle of particles) { - particle.update(canvas.width, canvas.height) - particle.draw(ctx, isDark) - } - - drawConnections(isDark) - - animationFrameId = requestAnimationFrame(animate) - } - resize() - initParticles() - animate() - window.addEventListener("resize", () => { - resize() - initParticles() + // Observe theme changes + const observer = new MutationObserver(() => { + draw() + }) + observer.observe(document.documentElement, { + attributes: true, + attributeFilter: ["class"], }) + window.addEventListener("resize", resize) + return () => { - cancelAnimationFrame(animationFrameId) + observer.disconnect() window.removeEventListener("resize", resize) } }, [])