Spaces:
Sleeping
Sleeping
| import React, { useEffect, useRef } from 'react'; | |
| const WaveCanvas = () => { | |
| const canvasRef = useRef(null); | |
| useEffect(() => { | |
| const canvas = canvasRef.current; | |
| if (!canvas) return; | |
| const ctx = canvas.getContext('2d'); | |
| if (!ctx) return; | |
| let animationFrameId; | |
| // Set canvas size | |
| const setCanvasSize = () => { | |
| canvas.width = window.innerWidth; | |
| canvas.height = window.innerHeight; | |
| }; | |
| // Initial setup | |
| setCanvasSize(); | |
| // Wave parameters | |
| const waves = [ | |
| { amplitude: 120, frequency: 0.001, speed: 0.0005, phase: 0 }, | |
| { amplitude: 80, frequency: 0.002, speed: 0.0004, phase: 2 } | |
| ]; | |
| // Get theme colors | |
| const getColors = () => { | |
| const isDarkTheme = document.body.getAttribute('data-theme') === 'dark'; | |
| return { | |
| firstWave: isDarkTheme | |
| ? { r: 147, g: 51, b: 234 } // Purple for dark theme | |
| : { r: 18, g: 163, b: 176 }, // #12A3B0 Bright teal | |
| secondWave: isDarkTheme | |
| ? { r: 59, g: 130, b: 246 } // Blue for dark theme | |
| : { r: 1, g: 73, b: 81 } // #014951 Medium teal | |
| }; | |
| }; | |
| // Draw function | |
| const draw = () => { | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| // Center the waves vertically | |
| const centerY = canvas.height * 0.5; | |
| const colors = getColors(); | |
| waves.forEach((wave, index) => { | |
| // Update wave phase | |
| wave.phase += wave.speed; | |
| // Create gradient with theme-aware colors | |
| const gradient = ctx.createLinearGradient(0, centerY - wave.amplitude, 0, centerY + wave.amplitude); | |
| const color = index === 0 ? colors.firstWave : colors.secondWave; | |
| if (document.body.getAttribute('data-theme') === 'dark') { | |
| gradient.addColorStop(0, `rgba(${color.r}, ${color.g}, ${color.b}, 0)`); | |
| gradient.addColorStop(0.5, `rgba(${color.r}, ${color.g}, ${color.b}, 0.8)`); | |
| gradient.addColorStop(1, `rgba(${color.r}, ${color.g}, ${color.b}, 0)`); | |
| } else { | |
| // Light theme gradient with additional color stops | |
| gradient.addColorStop(0, `rgba(${color.r}, ${color.g}, ${color.b}, 0)`); | |
| gradient.addColorStop(0.2, `rgba(1, 49, 53, 0.4)`); // #013135 | |
| gradient.addColorStop(0.5, `rgba(${color.r}, ${color.g}, ${color.b}, 0.8)`); | |
| gradient.addColorStop(0.8, `rgba(175, 221, 229, 0.4)`); // #AFDDE5 | |
| gradient.addColorStop(1, `rgba(${color.r}, ${color.g}, ${color.b}, 0)`); | |
| } | |
| // Begin drawing wave | |
| ctx.beginPath(); | |
| // Start from bottom left | |
| ctx.moveTo(0, canvas.height); | |
| ctx.lineTo(0, centerY); | |
| // Draw wave path | |
| for (let x = 0; x <= canvas.width; x++) { | |
| const y = centerY + | |
| Math.sin(x * wave.frequency + wave.phase) * wave.amplitude; | |
| ctx.lineTo(x, y); | |
| } | |
| // Complete the path to bottom right | |
| ctx.lineTo(canvas.width, centerY); | |
| ctx.lineTo(canvas.width, canvas.height); | |
| ctx.closePath(); | |
| // Fill with gradient | |
| ctx.fillStyle = gradient; | |
| ctx.fill(); | |
| }); | |
| animationFrameId = requestAnimationFrame(draw); | |
| }; | |
| // Start animation | |
| draw(); | |
| // Handle resize and theme changes | |
| const handleResize = () => { | |
| setCanvasSize(); | |
| }; | |
| window.addEventListener('resize', handleResize); | |
| // Watch for theme changes | |
| const observer = new MutationObserver(() => { | |
| draw(); // Redraw when theme changes | |
| }); | |
| observer.observe(document.body, { | |
| attributes: true, | |
| attributeFilter: ['data-theme'] | |
| }); | |
| // Cleanup | |
| return () => { | |
| window.removeEventListener('resize', handleResize); | |
| cancelAnimationFrame(animationFrameId); | |
| observer.disconnect(); | |
| }; | |
| }, []); | |
| return <canvas ref={canvasRef} className="wave-canvas" />; | |
| }; | |
| export default WaveCanvas; |