feat: add AnimatedBackground component and enhance UI with background effects
This commit is contained in:
parent
6e3b86df24
commit
2202bd606f
15
src/App.tsx
15
src/App.tsx
@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
|
|||||||
import PetDisplay from './components/PetDisplay';
|
import PetDisplay from './components/PetDisplay';
|
||||||
import InteractionMenu from './components/InteractionMenu';
|
import InteractionMenu from './components/InteractionMenu';
|
||||||
import PetRegister from './components/PetRegister';
|
import PetRegister from './components/PetRegister';
|
||||||
|
import AnimatedBackground from './components/AnimatedBackground';
|
||||||
import { Pet } from './types/Pet';
|
import { Pet } from './types/Pet';
|
||||||
import { fetchPets } from './services/api/api';
|
import { fetchPets } from './services/api/api';
|
||||||
|
|
||||||
@ -32,11 +33,20 @@ export default function App() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!pet) {
|
if (!pet) {
|
||||||
return <PetRegister onPetCreated={setPet} />;
|
return (
|
||||||
|
<>
|
||||||
|
<AnimatedBackground />
|
||||||
|
<div className="relative z-10">
|
||||||
|
<PetRegister onPetCreated={setPet} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-900 text-white p-4">
|
<>
|
||||||
|
<AnimatedBackground />
|
||||||
|
<div className="relative z-10 min-h-screen backdrop-blur-sm bg-gray-900/20 text-white p-4">
|
||||||
<div className="max-w-4xl mx-auto grid gap-8">
|
<div className="max-w-4xl mx-auto grid gap-8">
|
||||||
<PetDisplay pet={pet} />
|
<PetDisplay pet={pet} />
|
||||||
<InteractionMenu
|
<InteractionMenu
|
||||||
@ -46,5 +56,6 @@ export default function App() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
105
src/components/AnimatedBackground.tsx
Normal file
105
src/components/AnimatedBackground.tsx
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
export default function AnimatedBackground() {
|
||||||
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const canvas = canvasRef.current;
|
||||||
|
if (!canvas) return;
|
||||||
|
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = window.innerHeight;
|
||||||
|
|
||||||
|
const particles: Particle[] = [];
|
||||||
|
const particleCount = 50;
|
||||||
|
|
||||||
|
class Particle {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
size: number;
|
||||||
|
speedX: number;
|
||||||
|
speedY: number;
|
||||||
|
opacity: number;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.x = Math.random() * canvas.width;
|
||||||
|
this.y = Math.random() * canvas.height;
|
||||||
|
this.size = Math.random() * 3 + 1;
|
||||||
|
this.speedX = Math.random() * 1 - 0.5;
|
||||||
|
this.speedY = Math.random() * 1 - 0.5;
|
||||||
|
this.opacity = Math.random() * 0.5 + 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.x += this.speedX;
|
||||||
|
this.y += this.speedY;
|
||||||
|
|
||||||
|
if (this.x > canvas.width) this.x = 0;
|
||||||
|
if (this.x < 0) this.x = canvas.width;
|
||||||
|
if (this.y > canvas.height) this.y = 0;
|
||||||
|
if (this.y < 0) this.y = canvas.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
if (!ctx) return;
|
||||||
|
ctx.fillStyle = `rgba(255, 255, 255, ${this.opacity})`;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create particles
|
||||||
|
for (let i = 0; i < particleCount; i++) {
|
||||||
|
particles.push(new Particle());
|
||||||
|
}
|
||||||
|
|
||||||
|
function connectParticles() {
|
||||||
|
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 < 100) {
|
||||||
|
if (!ctx) return;
|
||||||
|
ctx.strokeStyle = `rgba(255, 255, 255, ${0.2 * (1 - distance/100)})`;
|
||||||
|
ctx.lineWidth = 0.5;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(particles[i].x, particles[i].y);
|
||||||
|
ctx.lineTo(particles[j].x, particles[j].y);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
if (!ctx) return;
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
particles.forEach(particle => {
|
||||||
|
particle.update();
|
||||||
|
particle.draw();
|
||||||
|
});
|
||||||
|
|
||||||
|
connectParticles();
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
}
|
||||||
|
|
||||||
|
animate();
|
||||||
|
|
||||||
|
const handleResize = () => {
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = window.innerHeight;
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('resize', handleResize);
|
||||||
|
return () => window.removeEventListener('resize', handleResize);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <canvas ref={canvasRef} className="fixed top-0 left-0 w-full h-full -z-10" />;
|
||||||
|
}
|
@ -137,7 +137,7 @@ export default function InteractionMenu({ pet, onPetUpdate }: InteractionMenuPro
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
<div className="grid grid-cols-2 md:grid-cols-3 gap-4 bg-gray-900 p-6 rounded-xl shadow-xl">
|
||||||
{remainingCooldown !== null && (
|
{remainingCooldown !== null && (
|
||||||
<div className="col-span-2 md:col-span-3 text-center p-2 bg-yellow-900/30 border-2 border-yellow-500/50 rounded-lg">
|
<div className="col-span-2 md:col-span-3 text-center p-2 bg-yellow-900/30 border-2 border-yellow-500/50 rounded-lg">
|
||||||
<span className="text-yellow-200">Cooldown: {formatCooldownTime(remainingCooldown)}</span>
|
<span className="text-yellow-200">Cooldown: {formatCooldownTime(remainingCooldown)}</span>
|
||||||
|
@ -1,3 +1,18 @@
|
|||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
overflow-x: hidden;
|
||||||
|
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.backdrop-blur-sm {
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add smooth transitions */
|
||||||
|
.transition-all {
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user