'use client' import React, { useEffect, useRef } from 'react' export function AsciiShaderBackground({ className = '' }: { className?: string }) { const canvasRef = useRef(null) useEffect(() => { const canvas = canvasRef.current if (!canvas) return const ctx = canvas.getContext('2d') if (!ctx) return let animationFrameId: number // Minimalistisches, ruhiges ASCII-Array const chars = ['•', '∘', '+', '▪', '▫', ' '] const fontSize = 18 let cols = 0 let rows = 0 const mouse = { x: 0, y: 0, targetX: 0, targetY: 0 } let scrollProgress = 0 let targetScrollProgress = 0 const getDynamicHue = (): number => { try { const hex = getComputedStyle(document.documentElement).getPropertyValue('--primary-custom').trim() || '#2563eb' let c = hex.replace('#', '') if (c.length === 3) c = c.split('').map(x => x + x).join('') const r = parseInt(c.substring(0, 2), 16) / 255 const g = parseInt(c.substring(2, 4), 16) / 255 const b = parseInt(c.substring(4, 6), 16) / 255 const max = Math.max(r, g, b) const min = Math.min(r, g, b) let h = 0 if (max !== min) { const d = max - min switch (max) { case r: h = (g - b) / d + (g < b ? 6 : 0); break case g: h = (b - r) / d + 2; break case b: h = (r - g) / d + 4; break } h /= 6 } return Math.round(h * 360) } catch (e) { return 217 } } const resize = () => { const width = window.innerWidth const height = window.innerHeight const dpr = window.devicePixelRatio || 1 canvas.width = width * dpr canvas.height = height * dpr ctx.scale(dpr, dpr) cols = Math.ceil(width / fontSize) rows = Math.ceil(height / fontSize) if (mouse.x === 0 && mouse.y === 0) { mouse.x = width / 2 mouse.y = height / 2 mouse.targetX = width / 2 mouse.targetY = height / 2 } } const onPointerMove = (e: PointerEvent) => { mouse.targetX = e.clientX mouse.targetY = e.clientY } const onScroll = () => { const maxScroll = Math.max( document.body.scrollHeight - window.innerHeight, 1 ) targetScrollProgress = Math.min(Math.max(window.scrollY / maxScroll, 0), 1) } resize() window.addEventListener('resize', resize) window.addEventListener('pointermove', onPointerMove) window.addEventListener('scroll', onScroll, { passive: true }) const render = (time: number) => { mouse.x += (mouse.targetX - mouse.x) * 0.05 mouse.y += (mouse.targetY - mouse.y) * 0.05 scrollProgress += (targetScrollProgress - scrollProgress) * 0.05 ctx.clearRect(0, 0, canvas.width, canvas.height) // Beim Scrollen sofort ausblenden (vollständig weg ab 150px Scroll-Tiefe) const scrollFade = Math.max(1 - (window.scrollY / 150), 0) if (scrollFade <= 0) { animationFrameId = requestAnimationFrame(render) return } ctx.font = `${fontSize}px monospace` ctx.textAlign = 'center' ctx.textBaseline = 'middle' const timeSec = time * 0.001 const activeHue = getDynamicHue() for (let r = 0; r < rows; r++) { for (let c = 0; c < cols; c++) { const x = c * fontSize + fontSize / 2 const y = r * fontSize + fontSize / 2 const dx = x - mouse.x const dy = y - mouse.y const dist = Math.sqrt(dx * dx + dy * dy) const mouseInfluence = Math.exp(-dist / 250) const mouseDistortion = (Math.atan2(dy, dx) + (dx + dy) * 0.003) * mouseInfluence * 3.0 const waveX = Math.sin(c * 0.08 + timeSec * 0.8 + mouseDistortion) const waveY = Math.cos(r * 0.08 + timeSec * 0.6 + mouseDistortion) const flowValue = Math.sin(waveX + waveY + (c + r) * 0.04 + timeSec * 0.4) const normalizedFlow = (flowValue + 1) * 0.5 const charIndex = Math.floor(normalizedFlow * chars.length) % chars.length const char = chars[charIndex] if (char === ' ') continue const blendFactor = Math.min(Math.max(mouseInfluence * 0.85 + normalizedFlow * 0.15, 0), 1) const alpha = (0.35 + blendFactor * 0.6) * scrollFade const lightness = 40 + Math.round(blendFactor * 50) ctx.fillStyle = `hsl(${activeHue} 80% ${lightness}% / ${alpha.toFixed(2)})` ctx.fillText(char, x, y) } } animationFrameId = requestAnimationFrame(render) } animationFrameId = requestAnimationFrame(render) return () => { window.removeEventListener('resize', resize) window.removeEventListener('pointermove', onPointerMove) window.removeEventListener('scroll', onScroll) cancelAnimationFrame(animationFrameId) } }, []) return (
) }