veza/apps/web/src/components/ui/AstralBackground.tsx
senke 93c15f7cae fix: Corriger URL Swagger et finaliser implémentation DeveloperPage
- Ajouter fallback pour Swagger UI si doc.json ne fonctionne pas
- Améliorer message d'erreur avec bouton pour ouvrir Swagger UI directement
- Les fonctionnalités API Keys et Usage Stats sont maintenant complètes et fonctionnelles
- Tous les onglets de DeveloperPage sont maintenant implémentés
2026-01-18 13:55:28 +01:00

129 lines
3.7 KiB
TypeScript

import { useEffect, useRef } from 'react';
/**
* Composant de fond animé "Astral"
* Crée un effet de particules et de nébuleuse subtil
*/
export function AstralBackground() {
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 particles: {
x: number;
y: number;
size: number;
speedX: number;
speedY: number;
opacity: number;
}[] = [];
// Configuration
const particleCount = 50;
const connectionDistance = 150;
const resize = () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
};
const createParticles = () => {
particles = [];
for (let i = 0; i < particleCount; i++) {
particles.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
size: Math.random() * 2,
speedX: (Math.random() - 0.5) * 0.2,
speedY: (Math.random() - 0.5) * 0.2,
opacity: Math.random() * 0.5 + 0.1,
});
}
};
const animate = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Update and draw particles
particles.forEach((p, i) => {
p.x += p.speedX;
p.y += p.speedY;
// Wrap around screen
if (p.x < 0) p.x = canvas.width;
if (p.x > canvas.width) p.x = 0;
if (p.y < 0) p.y = canvas.height;
if (p.y > canvas.height) p.y = 0;
// Draw particle
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
ctx.fillStyle = `rgba(102, 252, 241, ${p.opacity})`; // Cyan tint
ctx.fill();
// Draw connections
for (let j = i + 1; j < particles.length; j++) {
const p2 = particles[j];
const dx = p.x - p2.x;
const dy = p.y - p2.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < connectionDistance) {
ctx.beginPath();
ctx.strokeStyle = `rgba(102, 252, 241, ${0.1 * (1 - distance / connectionDistance)})`;
ctx.lineWidth = 0.5;
ctx.moveTo(p.x, p.y);
ctx.lineTo(p2.x, p2.y);
ctx.stroke();
}
}
});
animationFrameId = requestAnimationFrame(animate);
};
window.addEventListener('resize', resize);
resize();
createParticles();
animate();
return () => {
window.removeEventListener('resize', resize);
cancelAnimationFrame(animationFrameId);
};
}, []);
return (
<div className="fixed inset-0 z-0 pointer-events-none overflow-hidden">
{/* Deep Space Gradient */}
<div className="absolute inset-0 bg-gradient-to-b from-kodo-void via-[#0d1016] to-[#080a0c]" />
{/* Nebulas */}
<div className="absolute top-[-20%] left-[-10%] w-[50%] h-[50%] rounded-full bg-kodo-cyan/5 blur-[120px] animate-pulse" />
<div
className="absolute bottom-[-20%] right-[-10%] w-[50%] h-[50%] rounded-full bg-kodo-magenta/5 blur-[120px] animate-pulse"
style={{ animationDelay: '2s' }}
/>
{/* Star Field Canvas */}
<canvas ref={canvasRef} className="absolute inset-0 opacity-40" />
{/* Grid Overlay - FIX: Removed vertical gradient (90deg) that was creating visible vertical lines */}
{/* Only show horizontal grid lines to avoid vertical line artifacts */}
<div
className="absolute inset-0 opacity-[0.03]"
style={{
backgroundImage:
'linear-gradient(rgba(102, 252, 241, 0.5) 1px, transparent 1px)',
backgroundSize: '50px 50px',
}}
/>
</div>
);
}