UNPKG

aura-glass

Version:

A comprehensive glassmorphism design system for React applications with 142+ production-ready components

425 lines (422 loc) 16.3 kB
'use client'; import { jsxs, jsx } from 'react/jsx-runtime'; import { useReducedMotion } from '../../hooks/useReducedMotion.js'; import { forwardRef, useRef, useState, useEffect, useMemo } from 'react'; import { motion } from 'framer-motion'; import '../../primitives/GlassCore.js'; import '../../primitives/glass/GlassAdvanced.js'; import { OptimizedGlassCore } from '../../primitives/OptimizedGlassCore.js'; import '../../primitives/glass/OptimizedGlassAdvanced.js'; import '../../primitives/MotionNative.js'; import '../../primitives/motion/MotionFramer.js'; import { useA11yId } from '../../utils/a11y.js'; import { useMotionPreference } from '../../hooks/useMotionPreference.js'; import { createGlassStyle } from '../../utils/createGlassStyle.js'; import { cn } from '../../lib/utilsComprehensive.js'; const waveTypes = { sine: (x, t, amplitude, frequency, phase, k, velocity) => amplitude * Math.sin(k * x - 2 * Math.PI * frequency * t + phase), cosine: (x, t, amplitude, frequency, phase, k, velocity) => amplitude * Math.cos(k * x - 2 * Math.PI * frequency * t + phase), gaussian: (x, t, amplitude, frequency, phase, k, velocity) => amplitude * Math.exp(-Math.pow(x - velocity * t, 2) / 20) * Math.sin(k * x - 2 * Math.PI * frequency * t + phase), complex: (x, t, amplitude, frequency, phase, k, velocity) => { const real = amplitude * Math.cos(k * x - 2 * Math.PI * frequency * t + phase); const imag = amplitude * Math.sin(k * x - 2 * Math.PI * frequency * t + phase); return Math.sqrt(real * real + imag * imag); }, standing: (x, t, amplitude, frequency, phase, k, velocity) => amplitude * Math.sin(k * x + phase) * Math.cos(2 * Math.PI * frequency * t), traveling: (x, t, amplitude, frequency, phase, k, velocity) => amplitude * Math.sin(k * x - 2 * Math.PI * frequency * t + phase) }; const GlassWaveFunction = /*#__PURE__*/forwardRef(({ width = 800, height = 400, waveEquations, showInterference = true, showPhaseSpace = false, showAmplitude = true, showFrequencySpectrum = false, animationSpeed = 1, resolution = 2, timeScale = 1, showGrid = true, showLabels = true, realTimeMode = true, onWaveInteraction, className = "", ...props }, ref) => { useReducedMotion(); const canvasRef = useRef(null); const phaseCanvasRef = useRef(null); const spectrumCanvasRef = useRef(null); const [currentTime, setCurrentTime] = useState(0); const [selectedWave, setSelectedWave] = useState(null); const [waveData, setWaveData] = useState({}); useA11yId("glass-wave-function"); useMotionPreference(); // Time evolution useEffect(() => { if (!realTimeMode) return; const interval = setInterval(() => { setCurrentTime(prev => prev + 0.1 * animationSpeed * timeScale); }, 16); return () => clearInterval(interval); }, [realTimeMode, animationSpeed, timeScale]); // Generate wave data useMemo(() => { const points = Math.floor(width / resolution); const newWaveData = {}; waveEquations.forEach(wave => { const k = 2 * Math.PI / wave.wavelength; const data = []; for (let i = 0; i < points; i++) { const x = i / points * width - width / 2; // Center the wave const waveFunc = waveTypes[wave.type]; let y = waveFunc(x, currentTime, wave.amplitude, wave.frequency, wave.phase, k, wave.velocity); // Apply damping if present if (wave.damping && wave.damping > 0) { y *= Math.exp(-wave.damping * Math.abs(x)); } data.push(y); } newWaveData[wave.id] = data; }); setWaveData(newWaveData); return newWaveData; }, [waveEquations, currentTime, width, resolution]); // Main canvas rendering useEffect(() => { const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext("2d"); if (!ctx) return; ctx.clearRect(0, 0, width, height); // Grid if (showGrid) { ctx.strokeStyle = "var(--glass-bg-default)"; ctx.lineWidth = 1; // Horizontal grid lines for (let y = 0; y <= height; y += 40) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(width, y); ctx.stroke(); } // Vertical grid lines for (let x = 0; x <= width; x += 40) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, height); ctx.stroke(); } // Center line ctx.strokeStyle = "var(--glass-bg-hover)"; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(0, height / 2); ctx.lineTo(width, height / 2); ctx.stroke(); } // Draw individual waves Object.entries(waveData).forEach(([waveId, data]) => { const wave = waveEquations.find(w => w.id === waveId); if (!wave) return; ctx.strokeStyle = wave.color; ctx.lineWidth = selectedWave === waveId ? 3 : 2; ctx.globalAlpha = selectedWave && selectedWave !== waveId ? 0.3 : 0.8; ctx.beginPath(); data.forEach((y, i) => { const x = i / data.length * width; const canvasY = height / 2 - y * (height / 4); // Scale and center if (i === 0) { ctx.moveTo(x, canvasY); } else { ctx.lineTo(x, canvasY); } }); ctx.stroke(); // Wave labels if (showLabels) { ctx.fillStyle = wave.color; ctx.font = "12px Arial"; ctx.globalAlpha = 1; ctx.fillText(wave.name, 10, 20 + Object.keys(waveData).indexOf(waveId) * 20); } }); // Interference pattern if (showInterference && waveEquations.length > 1) { const interferenceData = []; const dataLength = Math.min(...Object.values(waveData).map(d => d.length)); for (let i = 0; i < dataLength; i++) { const sum = Object.values(waveData).reduce((total, data) => total + (data[i] || 0), 0); interferenceData.push(sum); } ctx.strokeStyle = "var(--glass-white)"; ctx.lineWidth = 2; ctx.globalAlpha = 0.9; ctx.setLineDash([5, 5]); ctx.beginPath(); interferenceData.forEach((y, i) => { const x = i / interferenceData.length * width; const canvasY = height / 2 - y * (height / 4); if (i === 0) { ctx.moveTo(x, canvasY); } else { ctx.lineTo(x, canvasY); } }); ctx.stroke(); ctx.setLineDash([]); } ctx.globalAlpha = 1; }, [waveData, waveEquations, width, height, showGrid, showLabels, showInterference, selectedWave]); // Phase space visualization useEffect(() => { if (!showPhaseSpace) return; const canvas = phaseCanvasRef.current; if (!canvas) return; const ctx = canvas.getContext("2d"); if (!ctx) return; const phaseWidth = 200; const phaseHeight = 200; ctx.clearRect(0, 0, phaseWidth, phaseHeight); // Grid for phase space ctx.strokeStyle = "var(--glass-bg-default)"; ctx.lineWidth = 1; for (let i = 0; i <= 10; i++) { const x = i / 10 * phaseWidth; const y = i / 10 * phaseHeight; ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, phaseHeight); ctx.moveTo(0, y); ctx.lineTo(phaseWidth, y); ctx.stroke(); } // Draw phase trajectories waveEquations.forEach((wave, index) => { const data = waveData[wave.id]; if (!data) return; ctx.strokeStyle = wave.color; ctx.lineWidth = 2; ctx.globalAlpha = 0.7; ctx.beginPath(); data.forEach((amplitude, i) => { if (i === 0) return; const prevAmplitude = data[i - 1]; const velocity = amplitude - prevAmplitude; // Approximate velocity const x = (amplitude + 2) / 4 * phaseWidth; // Normalize to canvas const y = phaseHeight - (velocity + 2) / 4 * phaseHeight; if (i === 1) { ctx.moveTo(x, y); } else { ctx.lineTo(x, y); } }); ctx.stroke(); }); ctx.globalAlpha = 1; }, [showPhaseSpace, waveData, waveEquations]); // Frequency spectrum useEffect(() => { if (!showFrequencySpectrum) return; const canvas = spectrumCanvasRef.current; if (!canvas) return; const ctx = canvas.getContext("2d"); if (!ctx) return; const spectrumWidth = 300; const spectrumHeight = 150; ctx.clearRect(0, 0, spectrumWidth, spectrumHeight); // Draw frequency bars waveEquations.forEach((wave, index) => { const barWidth = spectrumWidth / waveEquations.length; const barHeight = wave.amplitude / 2 * spectrumHeight; const x = index * barWidth; const y = spectrumHeight - barHeight; ctx.fillStyle = wave.color; ctx.globalAlpha = 0.7; ctx.fillRect(x + 2, y, barWidth - 4, barHeight); // Frequency label ctx.fillStyle = "var(--glass-white)"; ctx.font = "10px Arial"; ctx.globalAlpha = 1; ctx.fillText(`${wave.frequency.toFixed(1)}Hz`, x + 5, spectrumHeight - 5); }); }, [showFrequencySpectrum, waveEquations]); const handleCanvasClick = e => { const canvas = canvasRef.current; if (!canvas) return; const rect = canvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; // Find closest wave let closestWave = null; let minDistance = Infinity; Object.entries(waveData).forEach(([waveId, data]) => { const dataIndex = Math.floor(x / width * data.length); const waveY = height / 2 - data[dataIndex] * (height / 4); const distance = Math.abs(y - waveY); if (distance < minDistance && distance < 30) { minDistance = distance; closestWave = waveId; } }); if (closestWave) { setSelectedWave(prev => prev === closestWave ? null : closestWave); onWaveInteraction?.(closestWave, { x, y }); } }; return jsxs(OptimizedGlassCore, { ref: ref, variant: "frosted", className: cn("glass-p-4 glass-space-y-4", className), ...props, children: [jsxs("div", { className: "glass-flex glass-items-center glass-justify-between", children: [jsxs("div", { children: [jsx("h3", { className: cn("glass-text-lg glass-font-semibold glass-text-primary"), children: "Wave Function Visualization" }), jsxs("p", { className: cn("glass-text-sm glass-text-secondary"), children: [waveEquations.length, " wave", waveEquations.length !== 1 ? "s" : "", selectedWave && ` • Selected: ${waveEquations.find(w => w.id === selectedWave)?.name}`] })] }), jsxs("div", { className: cn("glass-flex glass-items-center glass-space-x-4"), children: [realTimeMode && jsxs("div", { className: cn("glass-flex glass-items-center glass-space-x-1 glass-text-success"), children: [jsx("div", { className: cn("glass-w-2 glass-h-2 glass-surface-success glass-radius-full glass-animate-pulse") }), jsx("span", { className: cn("glass-text-xs"), children: "Live" })] }), jsxs("div", { className: cn("glass-text-sm glass-text-secondary"), children: ["t = ", currentTime.toFixed(1)] })] })] }), jsxs("div", { className: cn("glass-relative"), children: [jsx("canvas", { ref: canvasRef, width: width, height: height, className: cn("glass-border glass-border-primary glass-radius-lg glass-surface-dark glass-cursor-crosshair"), onClick: handleCanvasClick }), showAmplitude && jsxs("div", { className: cn("glass-absolute glass-left-2 glass-top-2 glass-space-y-1"), children: [jsx("div", { className: cn("glass-text-xs glass-text-secondary"), children: "Amplitude" }), jsx("div", { className: cn("glass-text-xs glass-text-success"), children: "+2" }), jsx("div", { className: cn("glass-text-xs glass-text-muted"), style: { marginTop: height / 4 - 20 }, children: "0" }), jsx("div", { className: cn("glass-text-xs glass-text-danger"), style: { marginTop: height / 2 - 40 }, children: "-2" })] })] }), jsxs("div", { className: cn("glass-flex glass-space-x-4"), children: [showPhaseSpace && jsxs("div", { children: [jsx("h4", { className: cn("glass-text-sm glass-font-medium glass-text-primary glass-mb-2"), children: "Phase Space" }), jsx("canvas", { ref: phaseCanvasRef, width: 200, height: 200, className: cn("glass-border glass-border-primary glass-radius glass-surface-dark") }), jsx("div", { className: cn("glass-text-xs glass-text-muted glass-mt-1"), children: "Position vs Velocity" })] }), showFrequencySpectrum && jsxs("div", { children: [jsx("h4", { className: cn("glass-text-sm glass-font-medium glass-text-primary glass-mb-2"), children: "Frequency Spectrum" }), jsx("canvas", { ref: spectrumCanvasRef, width: 300, height: 150, className: cn("glass-border glass-border-primary glass-radius glass-surface-dark") })] })] }), jsxs("div", { className: cn("glass-p-3 glass-radius-lg glass-border glass-border-subtle glass-space-y-3", createGlassStyle({ blur: "sm", opacity: 0.6 }).background), children: [jsx("h4", { className: cn("glass-text-sm glass-font-semibold glass-text-primary"), children: "Wave Parameters" }), jsx("div", { className: cn("glass-grid glass-gap-2"), children: waveEquations.map(wave => jsx(motion.div, { className: cn("glass-p-2 glass-radius glass-border glass-transition-colors glass-duration-200 glass-cursor-pointer", selectedWave === wave.id ? "glass-border-primary glass-surface-subtle" : "glass-border-muted hover:glass-border-primary"), onClick: () => setSelectedWave(prev => prev === wave.id ? null : wave.id), whileHover: { scale: 1.02 }, whileTap: { scale: 0.98 }, children: jsxs("div", { className: cn("glass-flex glass-items-center glass-justify-between"), children: [jsxs("div", { className: cn("glass-flex glass-items-center glass-space-x-2"), children: [jsx("div", { className: cn("glass-w-3 glass-h-3 glass-radius-full"), style: { backgroundColor: wave.color } }), jsx("span", { className: cn("glass-text-sm glass-font-medium glass-text-primary"), children: wave.name }), jsxs("span", { className: cn("glass-text-xs glass-text-muted glass-capitalize"), children: ["(", wave.type, ")"] })] }), jsxs("div", { className: cn("glass-flex glass-items-center glass-space-x-4 glass-text-xs glass-text-secondary"), children: [jsxs("span", { children: ["A: ", wave.amplitude.toFixed(1)] }), jsxs("span", { children: ["f: ", wave.frequency.toFixed(1), "Hz"] }), jsxs("span", { children: ["\u03BB: ", wave.wavelength.toFixed(0)] }), jsxs("span", { children: ["v: ", wave.velocity.toFixed(0)] })] })] }) }, wave.id)) })] }), jsxs("div", { className: cn("glass-flex glass-items-center glass-justify-between glass-text-sm glass-text-secondary"), children: [jsxs("span", { children: ["Click waves to select \u2022 Time: ", timeScale, "x speed"] }), jsxs("div", { className: cn("glass-flex glass-items-center glass-space-x-4"), children: [jsxs("span", { children: ["Resolution: ", resolution, "px"] }), jsxs("span", { children: ["Animation: ", animationSpeed, "x"] })] })] })] }); }); export { GlassWaveFunction }; //# sourceMappingURL=GlassWaveFunction.js.map