UNPKG

aura-glass

Version:

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

621 lines (618 loc) 23.2 kB
'use client'; import { jsx, jsxs } from 'react/jsx-runtime'; import { forwardRef, useRef, useState, useCallback, useEffect } from 'react'; 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 { MotionFramer } from '../../primitives/motion/MotionFramer.js'; import { cn } from '../../lib/utilsComprehensive.js'; import { useA11yId } from '../../utils/a11y.js'; import { useMotionPreferenceContext } from '../../contexts/MotionPreferenceContext.js'; import { useGlassSound } from '../../utils/soundDesign.js'; const GlassNebulaClouds = /*#__PURE__*/forwardRef(({ width = 800, height = 600, nebulaType = "emission", density = 0.7, temperature = 10000, stellarWindStrength = 0.5, layerCount = 5, showStarClusters = true, showCosmicDust = true, animationSpeed = 1, turbulenceLevel = 0.6, colorIntensity = 0.8, cameraDistance = 1, timeScale = 1, ionizationLevel = 0.7, showEmissionLines = true, showMagneticField = false, onNebulaEvolution, onStarFormation, showControls = true, showNebulaInfo = true, respectMotionPreference = true, className, ...props }, ref) => { const { prefersReducedMotion, isMotionSafe } = useMotionPreferenceContext(); const { play } = useGlassSound(); const canvasRef = useRef(null); const animationRef = useRef(); const nebulaCloudsId = useA11yId("glass-nebula-clouds"); const [nebulaLayers, setNebulaLayers] = useState([]); const [starClusters, setStarClusters] = useState([]); const [cosmicDust, setCosmicDust] = useState([]); const [animationTime, setAnimationTime] = useState(0); const [nebulaAge, setNebulaAge] = useState(0); // millions of years const [evolutionPhase, setEvolutionPhase] = useState("formation"); // Nebula type configurations const nebulaConfigs = { emission: { colors: [[255, 100, 100], // H-alpha (red) [100, 255, 100], // OIII (green) [100, 100, 255], // H-beta (blue) [255, 255, 100] // Sodium (yellow) ], baseTemperature: 10000, ionizedGas: true }, reflection: { colors: [[150, 200, 255], // Blue scattered light [200, 220, 255], // White light reflection [120, 180, 255] // Rayleigh scattering ], baseTemperature: 3000, ionizedGas: false }, dark: { colors: [[50, 50, 50], // Dark molecular cloud [80, 60, 40], // Dust silhouette [40, 40, 60] // Cold regions ], baseTemperature: 10, ionizedGas: false }, planetary: { colors: [[100, 255, 255], // OIII (cyan) [255, 100, 255], // H-alpha (magenta) [255, 255, 100], // HeII (yellow) [100, 255, 100] // NII (green) ], baseTemperature: 50000, ionizedGas: true }, supernova: { colors: [[255, 255, 255], // Shock front [255, 200, 100], // Hot gas [255, 100, 100], // H-alpha [100, 200, 255] // Synchrotron radiation ], baseTemperature: 1000000, ionizedGas: true } }; // Temperature to color mapping const getTemperatureColor = useCallback(temp => { // Black body radiation approximation if (temp < 3000) return [255, 100, 0]; // Red if (temp < 5000) return [255, 200, 100]; // Orange if (temp < 7000) return [255, 255, 200]; // Yellow-white if (temp < 10000) return [200, 220, 255]; // White if (temp < 20000) return [150, 200, 255]; // Blue-white return [100, 150, 255]; // Blue }, []); // Generate nebula layers const generateNebulaLayers = useCallback(() => { const config = nebulaConfigs[nebulaType]; const layers = []; for (let i = 0; i < layerCount; i++) { const points = []; const layerDensity = density * (1 - i * 0.1); const layerTemp = temperature * (1 + (Math.random() - 0.5) * 0.3); // Generate cloud structure using Perlin-like noise for (let x = 0; x < width; x += 20) { for (let y = 0; y < height; y += 20) { const noise = Math.sin(x * 0.01 + i) * Math.cos(y * 0.01 + i) * Math.sin(x * y * 0.0001 + i * 2); if (Math.abs(noise) > 0.3) { points.push({ x: x + (Math.random() - 0.5) * 40, y: y + (Math.random() - 0.5) * 40, density: layerDensity * Math.abs(noise), temperature: layerTemp * (0.8 + Math.random() * 0.4) }); } } } layers.push({ name: `Layer ${i + 1}`, points, color: config.colors[i % config.colors.length], opacity: colorIntensity * (0.8 - i * 0.1), scale: 1 + i * 0.2, rotation: 0, rotationSpeed: (Math.random() - 0.5) * 0.001, turbulence: turbulenceLevel * (1 + i * 0.1), id: `nebula-layer-${i}` }); } setNebulaLayers(layers); }, [nebulaType, layerCount, density, temperature, width, height, colorIntensity, turbulenceLevel]); // Generate star clusters const generateStarClusters = useCallback(() => { if (!showStarClusters) return; const clusters = []; const clusterCount = Math.floor(3 + Math.random() * 4); for (let i = 0; i < clusterCount; i++) { const clusterX = Math.random() * width; const clusterY = Math.random() * height; const clusterDensity = Math.random() * 0.8 + 0.2; const clusterAge = Math.random() * 100; // million years const stars = []; const starCount = Math.floor(clusterDensity * 20 + 10); for (let j = 0; j < starCount; j++) { const angle = Math.random() * Math.PI * 2; const distance = Math.random() * 80; const spectralClass = ["O", "B", "A", "F", "G", "K", "M"][Math.floor(Math.random() * 7)]; // Star colors based on spectral class const starColors = { O: [150, 180, 255], // Blue B: [180, 200, 255], // Blue-white A: [220, 230, 255], // White F: [255, 245, 240], // Yellow-white G: [255, 255, 200], // Yellow (like Sun) K: [255, 200, 150], // Orange M: [255, 150, 100] // Red }; stars.push({ x: Math.cos(angle) * distance, y: Math.sin(angle) * distance, brightness: Math.random() * 0.8 + 0.2, color: starColors[spectralClass], size: spectralClass === "O" || spectralClass === "B" ? 3 : spectralClass === "M" ? 1 : 2, twinklePhase: Math.random() * Math.PI * 2, spectralClass }); } clusters.push({ x: clusterX, y: clusterY, stars, density: clusterDensity, age: clusterAge, id: `cluster-${i}` }); } setStarClusters(clusters); }, [showStarClusters, width, height]); // Generate cosmic dust const generateCosmicDust = useCallback(() => { if (!showCosmicDust) return; const dustClouds = []; const dustCloudCount = Math.floor(2 + Math.random() * 3); for (let i = 0; i < dustCloudCount; i++) { const particles = []; const particleCount = Math.floor(density * 100 + 50); const composition = ["silicate", "carbon", "ice", "organic"][Math.floor(Math.random() * 4)]; const dustColors = { silicate: [150, 120, 80], carbon: [80, 60, 40], ice: [200, 220, 255], organic: [120, 100, 60] }; for (let j = 0; j < particleCount; j++) { particles.push({ x: Math.random() * width, y: Math.random() * height, vx: (Math.random() - 0.5) * stellarWindStrength, vy: (Math.random() - 0.5) * stellarWindStrength, size: Math.random() * 2 + 0.5, opacity: Math.random() * 0.3 + 0.1, color: dustColors[composition], temperature: 50 + Math.random() * 200, lifetime: Math.random() * 10000 + 5000 }); } dustClouds.push({ particles, density: density * (0.5 + Math.random() * 0.5), composition, id: `dust-cloud-${i}` }); } setCosmicDust(dustClouds); }, [showCosmicDust, density, width, height, stellarWindStrength]); // Initialize nebula useEffect(() => { generateNebulaLayers(); generateStarClusters(); generateCosmicDust(); }, [generateNebulaLayers, generateStarClusters, generateCosmicDust]); // Update nebula layers const updateNebulaLayers = useCallback(deltaTime => { setNebulaLayers(prevLayers => prevLayers.map(layer => ({ ...layer, rotation: layer.rotation + layer.rotationSpeed * deltaTime * animationSpeed * timeScale, points: layer.points.map(point => ({ ...point, x: point.x + Math.sin(animationTime * 0.0001 + point.y * 0.01) * layer.turbulence * 0.1, y: point.y + Math.cos(animationTime * 0.0001 + point.x * 0.01) * layer.turbulence * 0.1, density: Math.max(0.1, point.density + (Math.random() - 0.5) * 0.05 * stellarWindStrength) })) }))); }, [animationSpeed, timeScale, animationTime, stellarWindStrength]); // Update cosmic dust const updateCosmicDust = useCallback(deltaTime => { setCosmicDust(prevDust => prevDust.map(dustCloud => ({ ...dustCloud, particles: dustCloud.particles.map(particle => ({ ...particle, x: particle.x + particle.vx * deltaTime * animationSpeed, y: particle.y + particle.vy * deltaTime * animationSpeed, lifetime: particle.lifetime - deltaTime, opacity: Math.max(0, particle.opacity - deltaTime * 0.00005) })).filter(particle => particle.lifetime > 0 && particle.x > -50 && particle.x < width + 50 && particle.y > -50 && particle.y < height + 50) }))); }, [animationSpeed, width, height]); // Nebula evolution simulation useEffect(() => { const evolutionInterval = setInterval(() => { setNebulaAge(prev => { const newAge = prev + timeScale * 0.1; // Determine evolution phase if (newAge < 10) { setEvolutionPhase("formation"); } else if (newAge < 50) { setEvolutionPhase("mature"); } else { setEvolutionPhase("dispersing"); } onNebulaEvolution?.(newAge, evolutionPhase); // Occasionally trigger star formation if (Math.random() < 0.05 * timeScale) { const location = { x: Math.random() * width, y: Math.random() * height }; onStarFormation?.(location); play("success"); } return newAge; }); }, 5000); return () => clearInterval(evolutionInterval); }, [timeScale, evolutionPhase, onNebulaEvolution, onStarFormation, width, height, play]); // Render nebula const render = useCallback(() => { const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext("2d"); if (!ctx) return; // Clear canvas with deep space background const spaceGradient = ctx.createRadialGradient(width / 2, height / 2, 0, width / 2, height / 2, Math.max(width, height) / 2); spaceGradient.addColorStop(0, "rgb(5, 5, 15)"); spaceGradient.addColorStop(1, "rgb(0, 0, 5)"); ctx.fillStyle = spaceGradient; ctx.fillRect(0, 0, width, height); // Draw background stars ctx.fillStyle = "white"; for (let i = 0; i < 100; i++) { const x = Math.random() * width; const y = Math.random() * height; const brightness = Math.random(); ctx.globalAlpha = brightness * 0.5; ctx.beginPath(); ctx.arc(x, y, brightness, 0, Math.PI * 2); ctx.fill(); } // Draw cosmic dust cosmicDust.forEach(dustCloud => { dustCloud.particles.forEach(particle => { if (particle.opacity < 0.01) return; ctx.globalAlpha = particle.opacity; ctx.fillStyle = `rgb(${particle.color[0]}, ${particle.color[1]}, ${particle.color[2]})`; ctx.beginPath(); ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2); ctx.fill(); }); }); // Draw nebula layers ctx.globalAlpha = 1; nebulaLayers.forEach((layer, layerIndex) => { ctx.save(); ctx.translate(width / 2, height / 2); ctx.rotate(layer.rotation); ctx.scale(layer.scale, layer.scale); ctx.translate(-width / 2, -height / 2); // Create nebula texture layer.points.forEach(point => { const adjustedDensity = point.density * density; if (adjustedDensity < 0.1) return; const size = Math.max(10, adjustedDensity * 30 / cameraDistance); const tempColor = ionizationLevel > 0.5 ? layer.color : getTemperatureColor(point.temperature); const gradient = ctx.createRadialGradient(point.x, point.y, 0, point.x, point.y, size); gradient.addColorStop(0, `rgba(${tempColor[0]}, ${tempColor[1]}, ${tempColor[2]}, ${layer.opacity * adjustedDensity})`); gradient.addColorStop(0.5, `rgba(${tempColor[0]}, ${tempColor[1]}, ${tempColor[2]}, ${layer.opacity * adjustedDensity * 0.5})`); gradient.addColorStop(1, `rgba(${tempColor[0]}, ${tempColor[1]}, ${tempColor[2]}, 0)`); ctx.fillStyle = gradient; ctx.beginPath(); ctx.arc(point.x, point.y, size, 0, Math.PI * 2); ctx.fill(); }); // Draw emission lines if (showEmissionLines && nebulaConfigs[nebulaType].ionizedGas) { ctx.globalAlpha = ionizationLevel * 0.3; ctx.strokeStyle = `rgb(${layer.color[0]}, ${layer.color[1]}, ${layer.color[2]})`; ctx.lineWidth = 1; for (let i = 0; i < layer.points.length - 1; i += 10) { const p1 = layer.points[i]; const p2 = layer.points[i + 1]; if (!p1 || !p2) continue; ctx.beginPath(); ctx.moveTo(p1.x, p1.y); ctx.lineTo(p2.x, p2.y); ctx.stroke(); } } ctx.restore(); }); // Draw magnetic field lines if (showMagneticField) { ctx.strokeStyle = "rgba(100, 255, 255, 0.2)"; ctx.lineWidth = 1; ctx.globalAlpha = 0.3; for (let x = 0; x < width; x += 100) { for (let y = 0; y < height; y += 100) { const fieldStrength = Math.sin(x * 0.01) * Math.cos(y * 0.01); if (Math.abs(fieldStrength) > 0.3) { ctx.beginPath(); ctx.moveTo(x, y); ctx.bezierCurveTo(x + 50, y + fieldStrength * 30, x + 80, y + fieldStrength * 20, x + 100, y); ctx.stroke(); } } } } // Draw star clusters starClusters.forEach(cluster => { cluster.stars.forEach(star => { const twinkle = Math.sin(animationTime * 0.005 + star.twinklePhase) * 0.3 + 0.7; ctx.globalAlpha = star.brightness * twinkle; // Create star glow const starGradient = ctx.createRadialGradient(cluster.x + star.x, cluster.y + star.y, 0, cluster.x + star.x, cluster.y + star.y, star.size * 3); starGradient.addColorStop(0, `rgb(${star.color[0]}, ${star.color[1]}, ${star.color[2]})`); starGradient.addColorStop(0.3, `rgba(${star.color[0]}, ${star.color[1]}, ${star.color[2]}, 0.8)`); starGradient.addColorStop(1, `rgba(${star.color[0]}, ${star.color[1]}, ${star.color[2]}, 0)`); ctx.fillStyle = starGradient; ctx.beginPath(); ctx.arc(cluster.x + star.x, cluster.y + star.y, star.size * 3, 0, Math.PI * 2); ctx.fill(); // Draw star core ctx.fillStyle = `rgb(${star.color[0]}, ${star.color[1]}, ${star.color[2]})`; ctx.beginPath(); ctx.arc(cluster.x + star.x, cluster.y + star.y, star.size, 0, Math.PI * 2); ctx.fill(); }); }); // Nebula info overlay if (showNebulaInfo) { ctx.save(); ctx.fillStyle = "var(--glass-text-secondary-dark)"; ctx.fillRect(10, 10, 250, 160); ctx.fillStyle = "white"; ctx.font = "14px sans-serif"; ctx.fillText(`Nebula Type: ${nebulaType}`, 20, 30); ctx.fillText(`Age: ${nebulaAge.toFixed(1)} million years`, 20, 50); ctx.fillText(`Phase: ${evolutionPhase}`, 20, 70); ctx.fillText(`Temperature: ${temperature.toLocaleString()} K`, 20, 90); ctx.fillText(`Density: ${Math.round(density * 100)}%`, 20, 110); ctx.fillText(`Ionization: ${Math.round(ionizationLevel * 100)}%`, 20, 130); ctx.fillText(`Star Clusters: ${starClusters.length}`, 20, 150); ctx.restore(); } }, [width, height, cosmicDust, nebulaLayers, density, cameraDistance, ionizationLevel, getTemperatureColor, showEmissionLines, nebulaType, showMagneticField, animationTime, starClusters, showNebulaInfo, nebulaAge, evolutionPhase, temperature]); // Animation loop useEffect(() => { if (prefersReducedMotion && respectMotionPreference) { render(); return; } const animate = currentTime => { const deltaTime = 16; // 60fps setAnimationTime(prev => prev + deltaTime); updateNebulaLayers(deltaTime); updateCosmicDust(deltaTime); render(); animationRef.current = requestAnimationFrame(animate); }; animationRef.current = requestAnimationFrame(animate); return () => { if (animationRef.current) { cancelAnimationFrame(animationRef.current); } }; }, [prefersReducedMotion, respectMotionPreference, render, updateNebulaLayers, updateCosmicDust]); // Canvas setup useEffect(() => { const canvas = canvasRef.current; if (!canvas) return; canvas.width = width; canvas.height = height; }, [width, height]); // Controls const renderControls = () => { if (!showControls) return null; return jsxs(OptimizedGlassCore, { elevation: "level2", intensity: "medium", depth: 1, tint: "neutral", border: "subtle", className: "glass-nebula-controls glass-flex glass-flex-wrap glass-items-center glass-gap-4 glass-p-4 glass-radius-lg glass-glass-glass-backdrop-blur-md glass-border glass-border-glass-border/20 glass-contrast-guard", children: [jsxs("div", { className: "glass-flex glass-items-center glass-gap-2", children: [jsx("label", { className: "glass-text-sm", children: "Type:" }), jsxs("select", { value: nebulaType, onChange: e => {}, className: "glass-px-2 glass-py-1 glass-radius-md glass-surface-overlay glass-border glass-border-glass-border/20 glass-contrast-guard glass-focus glass-touch-target", children: [jsx("option", { value: "emission", children: "Emission" }), jsx("option", { value: "reflection", children: "Reflection" }), jsx("option", { value: "dark", children: "Dark" }), jsx("option", { value: "planetary", children: "Planetary" }), jsx("option", { value: "supernova", children: "Supernova" })] })] }), jsxs("div", { className: "glass-flex glass-items-center glass-gap-2", children: [jsx("label", { className: "glass-text-sm", children: "Density:" }), jsx("input", { type: "range", min: "0.1", max: "1", step: "0.1", value: density, onChange: e => {}, className: 'w-20 glass-focus glass-touch-target glass-contrast-guard' })] }), jsxs("div", { className: "glass-flex glass-items-center glass-gap-2", children: [jsx("label", { className: "glass-text-sm", children: "Temperature:" }), jsx("input", { type: "range", min: "10", max: "100000", step: "1000", value: temperature, onChange: e => {}, className: 'w-20 glass-focus glass-touch-target glass-contrast-guard' }), jsxs("span", { className: "glass-text-xs", children: [(temperature / 1000).toFixed(1), "K K"] })] }), jsxs("div", { className: "glass-flex glass-items-center glass-gap-2", children: [jsx("label", { className: "glass-text-sm", children: "Time Scale:" }), jsx("input", { type: "range", min: "0.1", max: "10", step: "0.1", value: timeScale, onChange: e => {}, className: 'w-20 glass-focus glass-touch-target glass-contrast-guard' })] }), jsxs("div", { className: "glass-flex glass-items-center glass-gap-2", children: [jsxs("label", { className: "glass-text-sm", children: [jsx("input", { type: "checkbox", checked: showStarClusters, onChange: e => {}, className: "glass-mr-1 glass-focus glass-touch-target glass-contrast-guard" }), "Stars"] }), jsxs("label", { className: "glass-text-sm", children: [jsx("input", { type: "checkbox", checked: showCosmicDust, onChange: e => {}, className: "glass-mr-1 glass-focus glass-touch-target glass-contrast-guard" }), "Dust"] }), jsxs("label", { className: "glass-text-sm", children: [jsx("input", { type: "checkbox", checked: showEmissionLines, onChange: e => {}, className: "glass-mr-1 glass-focus glass-touch-target glass-contrast-guard" }), "Emission"] }), jsxs("label", { className: "glass-text-sm", children: [jsx("input", { type: "checkbox", checked: showMagneticField, onChange: e => {}, className: "glass-mr-1 glass-focus glass-touch-target glass-contrast-guard" }), "Magnetic"] })] })] }); }; return jsx(OptimizedGlassCore, { ref: ref, id: nebulaCloudsId, elevation: "level1", intensity: "subtle", depth: 1, tint: "neutral", border: "subtle", className: cn("glass-nebula-clouds relative glass-radius-lg glass-glass-backdrop-blur-md border border-border/20", className), ...props, children: jsxs(MotionFramer, { preset: isMotionSafe && respectMotionPreference ? "fadeIn" : "none", className: "glass-flex glass-flex-col glass-gap-4 glass-p-4", children: [renderControls(), jsx("div", { className: 'relative', children: jsx("canvas", { ref: canvasRef, width: width, height: height, className: "glass-border glass-border-glass-border/20 glass-radius-md glass-surface-dark glass-contrast-guard", style: { width, height } }) })] }) }); }); GlassNebulaClouds.displayName = "GlassNebulaClouds"; export { GlassNebulaClouds, GlassNebulaClouds as default }; //# sourceMappingURL=GlassNebulaClouds.js.map