mirror of
https://github.com/clucraft/PriceGhost.git
synced 2026-04-25 00:36:32 +02:00
Add particle background effect
- Pure CSS/JS particle animation behind all content - Multiple layers with different speeds for depth effect - Theme-aware: white particles on dark mode, purple on light mode - Low opacity for subtle, non-distracting ambiance - Uses box-shadow technique for performance Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
c4c05236c0
commit
2bbaab8793
2 changed files with 111 additions and 0 deletions
|
|
@ -2,6 +2,7 @@ import { ReactNode, useState, useEffect, useRef } from 'react';
|
|||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { useAuth } from '../hooks/useAuth';
|
||||
import NotificationBell from './NotificationBell';
|
||||
import ParticleBackground from './ParticleBackground';
|
||||
|
||||
interface LayoutProps {
|
||||
children: ReactNode;
|
||||
|
|
@ -293,6 +294,8 @@ export default function Layout({ children }: LayoutProps) {
|
|||
}
|
||||
`}</style>
|
||||
|
||||
<ParticleBackground />
|
||||
|
||||
<nav className="navbar">
|
||||
<div className="navbar-content">
|
||||
<Link to="/" className="navbar-brand">
|
||||
|
|
|
|||
108
frontend/src/components/ParticleBackground.tsx
Normal file
108
frontend/src/components/ParticleBackground.tsx
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
import { useMemo } from 'react';
|
||||
|
||||
// Generate random box-shadow particles
|
||||
function generateParticles(count: number, spacing: number, color: string): string {
|
||||
const shadows: string[] = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
const x = Math.floor(Math.random() * spacing);
|
||||
const y = Math.floor(Math.random() * spacing);
|
||||
shadows.push(`${x}px ${y}px ${color}`);
|
||||
}
|
||||
return shadows.join(', ');
|
||||
}
|
||||
|
||||
interface ParticleLayerProps {
|
||||
count: number;
|
||||
size: number;
|
||||
duration: number;
|
||||
color: string;
|
||||
spacing: number;
|
||||
}
|
||||
|
||||
function ParticleLayer({ count, size, duration, color, spacing }: ParticleLayerProps) {
|
||||
const boxShadow = useMemo(() => generateParticles(count, spacing, color), [count, spacing, color]);
|
||||
const boxShadowAfter = useMemo(() => generateParticles(Math.floor(count * 0.8), spacing, color), [count, spacing, color]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="particle-layer"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: `${size}px`,
|
||||
height: `${size}px`,
|
||||
background: 'transparent',
|
||||
boxShadow,
|
||||
borderRadius: '50%',
|
||||
animation: `particleFloat ${duration}s linear infinite`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: `${spacing}px`,
|
||||
left: 0,
|
||||
width: `${size}px`,
|
||||
height: `${size}px`,
|
||||
background: 'transparent',
|
||||
boxShadow: boxShadowAfter,
|
||||
borderRadius: '50%',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ParticleBackground() {
|
||||
const spacing = 2000;
|
||||
|
||||
return (
|
||||
<div className="particle-background">
|
||||
<style>{`
|
||||
.particle-background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@keyframes particleFloat {
|
||||
from {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
to {
|
||||
transform: translateY(-${spacing}px);
|
||||
}
|
||||
}
|
||||
|
||||
/* Light mode - subtle gray particles */
|
||||
[data-theme="light"] .particle-background {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
[data-theme="light"] .particle-layer {
|
||||
--particle-color: #6366f1;
|
||||
}
|
||||
|
||||
/* Dark mode - white particles */
|
||||
[data-theme="dark"] .particle-background {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .particle-layer {
|
||||
--particle-color: #ffffff;
|
||||
}
|
||||
`}</style>
|
||||
|
||||
{/* Multiple layers with different speeds for depth effect */}
|
||||
<ParticleLayer count={300} size={1} duration={80} color="var(--particle-color, #fff)" spacing={spacing} />
|
||||
<ParticleLayer count={200} size={2} duration={120} color="var(--particle-color, #fff)" spacing={spacing} />
|
||||
<ParticleLayer count={100} size={2} duration={160} color="var(--particle-color, #fff)" spacing={spacing} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue