diff --git a/src/App.css b/src/App.css index 0f06c55..c10ab04 100644 --- a/src/App.css +++ b/src/App.css @@ -13,7 +13,7 @@ body, html { /* Body and Background Enhancements */ body { font-family: 'Roboto', sans-serif; /* Modern font */ - background: linear-gradient(135deg, #1e3c72, #2a5298); /* Futuristic gradient background */ + /* Background is now handled by AnimatedGradientBackground component */ color: #e0e0e0; /* Light text for contrast */ overflow-x: hidden; /* Prevent horizontal scrolling */ line-height: 1.6; diff --git a/src/App.tsx b/src/App.tsx index ea8bdda..a6db668 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -14,6 +14,7 @@ import DeleteUser from './components/DeleteUser'; import NotFound from './components/NotFound'; import UserContext from './context/UserContext'; import OrderHistory from './components/OrderHistory'; +import AnimatedGradientBackground from './components/AnimatedGradientBackground'; import './App.css'; const queryClient = new QueryClient(); @@ -24,6 +25,7 @@ function App() { return ( + } /> diff --git a/src/components/AnimatedGradientBackground.css b/src/components/AnimatedGradientBackground.css new file mode 100644 index 0000000..4071857 --- /dev/null +++ b/src/components/AnimatedGradientBackground.css @@ -0,0 +1,58 @@ +/* Animated Gradient Background Styles */ + +.animated-gradient-background { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; + transition: background 0.3s ease-out; +} + +.ripples-container { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; + pointer-events: none; + overflow: hidden; +} + +.ripple { + position: absolute; + width: 0; + height: 0; + border-radius: 50%; + background: radial-gradient( + circle, + rgba(255, 255, 255, 0.3) 0%, + rgba(255, 255, 255, 0.1) 50%, + rgba(255, 255, 255, 0) 100% + ); + transform: translate(-50%, -50%); + animation: ripple-animation 1.5s ease-out forwards; +} + +@keyframes ripple-animation { + 0% { + width: 0; + height: 0; + opacity: 1; + } + 50% { + opacity: 0.6; + } + 100% { + width: 500px; + height: 500px; + opacity: 0; + } +} + +/* Ensure the background doesn't interfere with scrolling */ +body { + position: relative; +} diff --git a/src/components/AnimatedGradientBackground.tsx b/src/components/AnimatedGradientBackground.tsx new file mode 100644 index 0000000..1a37330 --- /dev/null +++ b/src/components/AnimatedGradientBackground.tsx @@ -0,0 +1,110 @@ +import React, { useState, useEffect, useRef } from 'react'; +import './AnimatedGradientBackground.css'; + +interface Ripple { + id: number; + x: number; + y: number; +} + +const AnimatedGradientBackground: React.FC = () => { + const [mousePosition, setMousePosition] = useState({ x: 50, y: 50 }); + const [ripples, setRipples] = useState([]); + const rippleIdRef = useRef(0); + const timeoutIdsRef = useRef>(new Set()); + const rafIdRef = useRef(null); + + useEffect(() => { + const handleMouseMove = (e: MouseEvent) => { + // Throttle mouse move updates using requestAnimationFrame + if (rafIdRef.current === null) { + rafIdRef.current = requestAnimationFrame(() => { + const x = (e.clientX / window.innerWidth) * 100; + const y = (e.clientY / window.innerHeight) * 100; + setMousePosition({ x, y }); + rafIdRef.current = null; + }); + } + }; + + const handleMouseClick = (e: MouseEvent) => { + const newRipple: Ripple = { + id: rippleIdRef.current++, + x: e.clientX, + y: e.clientY, + }; + + setRipples(prev => [...prev, newRipple]); + + // Remove ripple after animation completes, track timeout for cleanup + const timeoutId = setTimeout(() => { + setRipples(prev => prev.filter(r => r.id !== newRipple.id)); + timeoutIdsRef.current.delete(timeoutId); + }, 1500); + timeoutIdsRef.current.add(timeoutId); + }; + + window.addEventListener('mousemove', handleMouseMove); + window.addEventListener('click', handleMouseClick); + + return () => { + window.removeEventListener('mousemove', handleMouseMove); + window.removeEventListener('click', handleMouseClick); + + // Clean up pending timeouts + timeoutIdsRef.current.forEach(id => clearTimeout(id)); + timeoutIdsRef.current.clear(); + + // Clean up pending animation frame + if (rafIdRef.current !== null) { + cancelAnimationFrame(rafIdRef.current); + } + }; + }, []); + + // Calculate gradient colors based on mouse position + const calculateGradient = () => { + // Using a monochromatic blue palette + const baseHue = 220; // Blue hue + const saturation = 70; + + // Vary lightness based on mouse position + const lightness1 = 15 + (mousePosition.x / 100) * 20; // 15-35% + const lightness2 = 25 + (mousePosition.y / 100) * 25; // 25-50% + const lightness3 = 20 + ((mousePosition.x + mousePosition.y) / 200) * 30; // 20-50% + + return ` + radial-gradient( + circle at ${mousePosition.x}% ${mousePosition.y}%, + hsl(${baseHue}, ${saturation}%, ${lightness1}%) 0%, + hsl(${baseHue}, ${saturation - 10}%, ${lightness2}%) 40%, + hsl(${baseHue}, ${saturation - 20}%, ${lightness3}%) 100% + ) + `; + }; + + return ( + <> +
+
+ {ripples.map(ripple => ( +
+ ))} +
+ + ); +}; + +export default AnimatedGradientBackground;