aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
580 lines (564 loc) • 20.5 kB
JavaScript
'use client';
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
import { useReducedMotion } from '../../hooks/useReducedMotion.js';
import { Canvas, useThree, useFrame } from '@react-three/fiber';
import { motion } from 'framer-motion';
import { Flame, Pause, Play, Wind, Sparkles, Star, Palette, Zap, Waves } from 'lucide-react';
import { useRef, useState, useEffect, useCallback } from 'react';
import { cn } from '../../lib/utilsComprehensive.js';
import * as THREE from 'three';
// Aurora geometry and material factories
const AuroraFactory = {
// Create aurora wave geometry
createAuroraWave: (width = 20, height = 10, segments = 64) => {
const geometry = new THREE.PlaneGeometry(width, height, segments, 32);
// Add wave-like deformation
const positions = geometry.attributes.position.array;
for (let i = 0; i < positions.length; i += 3) {
const x = positions[i];
const y = positions[i + 1];
const wave = Math.sin(x * 0.2) * Math.cos(y * 0.3) * 2;
positions[i + 2] = wave;
}
geometry.computeVertexNormals();
return geometry;
},
// Create aurora curtain geometry
createAuroraCurtain: (width = 15, height = 8, depth = 3) => {
const geometry = new THREE.PlaneGeometry(width, height, 32, 16);
// Add depth variation
const positions = geometry.attributes.position.array;
for (let i = 0; i < positions.length; i += 3) {
const x = positions[i];
const y = positions[i + 1];
const depthVariation = Math.sin(x * 0.3) * Math.cos(y * 0.4) * depth;
positions[i + 2] = depthVariation;
}
geometry.computeVertexNormals();
return geometry;
},
// Create aurora particles
createAuroraParticles: (count = 100) => {
const geometry = new THREE.BufferGeometry();
const positions = new Float32Array(count * 3);
const colors = new Float32Array(count * 3);
const sizes = new Float32Array(count);
const colorPalette = [new THREE.Color(0x4fc3f7),
// Light blue
new THREE.Color(0x81c784),
// Light green
new THREE.Color(0xffb74d),
// Light orange
new THREE.Color(0xba68c8),
// Light purple
new THREE.Color(0xff8a65),
// Light red
new THREE.Color(0x4db6ac) // Light teal
];
for (let i = 0; i < count; i++) {
// Random position in aurora band
positions[i * 3] = (Math.random() - 0.5) * 25;
positions[i * 3 + 1] = Math.random() * 12 + 2;
positions[i * 3 + 2] = (Math.random() - 0.5) * 8;
// Random color from palette
const color = colorPalette[Math.floor(Math.random() * colorPalette.length)];
colors[i * 3] = color.r;
colors[i * 3 + 1] = color.g;
colors[i * 3 + 2] = color.b;
// Random size
sizes[i] = Math.random() * 3 + 1;
}
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
geometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1));
return geometry;
},
// Aurora wave material
createAuroraMaterial: (options = {}) => {
const {
color1 = new THREE.Color(0x4fc3f7),
color2 = new THREE.Color(0x81c784),
color3 = new THREE.Color(0xba68c8),
intensity = 1.0,
speed = 1.0
} = options;
return new THREE.ShaderMaterial({
uniforms: {
time: {
value: 0
},
color1: {
value: color1
},
color2: {
value: color2
},
color3: {
value: color3
},
intensity: {
value: intensity
},
speed: {
value: speed
}
},
vertexShader: `
varying vec3 vPosition;
varying vec2 vUv;
void main() {
vPosition = position;
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform float time;
uniform vec3 color1;
uniform vec3 color2;
uniform vec3 color3;
uniform float intensity;
uniform float speed;
varying vec3 vPosition;
varying vec2 vUv;
// Noise function for organic movement
float noise(vec2 st) {
const prefersReducedMotion = useReducedMotion();
return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
}
void main() {
vec2 st = vUv;
float n = noise(st * 3.0 + time * speed * 0.1);
// Create flowing aurora effect
float wave1 = sin(st.x * 6.0 + time * speed) * 0.5 + 0.5;
float wave2 = cos(st.y * 4.0 + time * speed * 0.7) * 0.5 + 0.5;
float wave3 = sin((st.x + st.y) * 3.0 + time * speed * 0.5) * 0.5 + 0.5;
// Mix colors based on waves
vec3 color = mix(color1, color2, wave1);
color = mix(color, color3, wave2);
color = mix(color, color1, wave3);
// Add some sparkle
float sparkle = sin(time * 10.0 + st.x * 20.0 + st.y * 20.0) * 0.5 + 0.5;
color += sparkle * 0.3;
float alpha = (wave1 + wave2 + wave3) / 3.0 * intensity * 0.8;
gl_FragColor = vec4(color, alpha);
}
`,
transparent: true,
side: THREE.DoubleSide,
blending: THREE.AdditiveBlending
});
},
// Aurora particle material
createParticleMaterial: () => {
return new THREE.ShaderMaterial({
uniforms: {
time: {
value: 0
}
},
vertexShader: `
attribute float size;
attribute vec3 color;
varying vec3 vColor;
void main() {
vColor = color;
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
gl_PointSize = size * (300.0 / -mvPosition.z);
gl_Position = projectionMatrix * mvPosition;
}
`,
fragmentShader: `
uniform float time;
varying vec3 vColor;
void main() {
float r = distance(gl_PointCoord, vec2(0.5, 0.5));
if (r > 0.5) discard;
float alpha = 1.0 - smoothstep(0.0, 0.5, r);
alpha *= sin(time * 3.0 + gl_PointCoord.x * 10.0) * 0.3 + 0.7;
gl_FragColor = vec4(vColor, alpha);
}
`,
transparent: true,
vertexColors: true,
blending: THREE.AdditiveBlending
});
}
};
// Aurora animation system
const AuroraAnimations = {
// Flowing wave animation
waveFlow: (mesh, time, speed = 1) => {
if (mesh.material && 'uniforms' in mesh.material && mesh.material.uniforms && typeof mesh.material.uniforms === 'object' && 'time' in mesh.material.uniforms) {
mesh.material.uniforms.time.value = time * speed;
}
},
// Particle drift animation
particleDrift: (particles, time, speed = 1) => {
if (!particles.geometry) return;
const positions = particles.geometry.attributes.position.array;
for (let i = 0; i < positions.length; i += 3) {
const originalX = positions[i];
positions[i] = originalX + Math.sin(time * speed + i * 0.01) * 0.5;
}
particles.geometry.attributes.position.needsUpdate = true;
},
// Color shift animation
colorShift: (mesh, time, speed = 1) => {
if (!mesh.material || !('uniforms' in mesh.material) || !mesh.material.uniforms) return;
const hue = time * speed * 0.1 % 1;
const color1 = new THREE.Color().setHSL(hue, 0.7, 0.6);
const color2 = new THREE.Color().setHSL((hue + 0.3) % 1, 0.7, 0.6);
const color3 = new THREE.Color().setHSL((hue + 0.6) % 1, 0.7, 0.6);
if (typeof mesh.material.uniforms === 'object' && 'color1' in mesh.material.uniforms) mesh.material.uniforms.color1.value = color1;
if (typeof mesh.material.uniforms === 'object' && 'color2' in mesh.material.uniforms) mesh.material.uniforms.color2.value = color2;
if (typeof mesh.material.uniforms === 'object' && 'color3' in mesh.material.uniforms) mesh.material.uniforms.color3.value = color3;
},
// Intensity pulsing
intensityPulse: (mesh, time, speed = 1) => {
if (!mesh.material || !('uniforms' in mesh.material) || !mesh.material.uniforms) return;
const pulse = Math.sin(time * speed) * 0.3 + 0.7;
if (typeof mesh.material.uniforms === 'object' && 'intensity' in mesh.material.uniforms) mesh.material.uniforms.intensity.value = pulse;
}
};
function AuroraPro({
intensity = 1.0,
speed = 1.0,
colorPalette = 'arctic',
customColors,
particleCount = 50,
showParticles = true,
showWaves = true,
showCurtain = false,
animationMode = 'flow',
className = '',
showControls = false,
autoAnimate = true,
onAnimationChange,
children
}) {
const prefersReducedMotion = useReducedMotion();
const canvasRef = useRef(null);
const [isPlaying, setIsPlaying] = useState(autoAnimate);
const [currentMode, setCurrentMode] = useState(animationMode);
const [auroraMeshes, setAuroraMeshes] = useState([]);
const [particleSystem, setParticleSystem] = useState(null);
// Color palette configurations
const colorPalettes = {
arctic: ['#4fc3f7', '#81c784', '#ba68c8'],
forest: ['#4caf50', '#8bc34a', '#009688'],
sunset: ['#ff9800', '#e91e63', '#9c27b0'],
ocean: ['#00bcd4', '#2196f3', '#3f51b5'],
cosmic: ['#9c27b0', '#673ab7', '#3f51b5'],
custom: customColors || ['var(--glass-white)', 'var(--glass-white)', 'var(--glass-white)']
};
// Initialize aurora elements
useEffect(() => {
const meshes = [];
const colors = colorPalettes[colorPalette].map(c => new THREE.Color(c));
// Create wave mesh
if (showWaves) {
const waveGeometry = AuroraFactory.createAuroraWave(25, 12, 64);
const waveMaterial = AuroraFactory.createAuroraMaterial({
color1: colors[0],
color2: colors[1],
color3: colors[2],
intensity,
speed
});
const waveMesh = new THREE.Mesh(waveGeometry, waveMaterial);
waveMesh.position.set(0, 5, -5);
meshes.push(waveMesh);
}
// Create curtain mesh
if (showCurtain) {
const curtainGeometry = AuroraFactory.createAuroraCurtain(20, 10, 2);
const curtainMaterial = AuroraFactory.createAuroraMaterial({
color1: colors[1],
color2: colors[2],
color3: colors[0],
intensity: intensity * 0.8,
speed: speed * 0.7
});
const curtainMesh = new THREE.Mesh(curtainGeometry, curtainMaterial);
curtainMesh.position.set(0, 3, -3);
meshes.push(curtainMesh);
}
// Create particle system
if (showParticles) {
const particleGeometry = AuroraFactory.createAuroraParticles(particleCount);
const particleMaterial = AuroraFactory.createParticleMaterial();
const particles = new THREE.Points(particleGeometry, particleMaterial);
setParticleSystem(particles);
}
setAuroraMeshes(meshes);
}, [colorPalette, intensity, speed, showWaves, showCurtain, showParticles, particleCount, customColors]);
// Handle animation mode changes
const changeAnimationMode = useCallback(mode => {
setCurrentMode(mode);
onAnimationChange?.(mode);
}, [onAnimationChange]);
// Toggle play/pause
const togglePlay = useCallback(() => {
setIsPlaying(prev => !prev);
}, []);
// Get animation mode icon
const getAnimationIcon = mode => {
switch (mode) {
case 'flow':
return jsx(Waves, {
className: cn("glass-w-4 glass-h-4")
});
case 'pulse':
return jsx(Zap, {
className: cn("glass-w-4 glass-h-4")
});
case 'shift':
return jsx(Palette, {
className: cn("glass-w-4 glass-h-4")
});
case 'mixed':
return jsx(Sparkles, {
className: cn("glass-w-4 glass-h-4")
});
default:
return jsx(Star, {
className: cn("glass-w-4 glass-h-4")
});
}
};
// Get color palette colors for UI
const getPaletteColors = palette => {
useReducedMotion();
return colorPalettes[palette] || colorPalettes.arctic;
};
return jsxs("div", {
className: cn("aurora-pro glass-relative glass-overflow-hidden", className),
children: [jsx(Canvas, {
ref: canvasRef,
className: cn("glass-absolute glass-inset-0 glass-pointer-events-none"),
camera: {
position: [0, 8, 15],
fov: 75
},
gl: {
alpha: true,
antialias: true,
powerPreference: 'high-performance'
},
children: jsx(AuroraScene, {
auroraMeshes: auroraMeshes,
particleSystem: particleSystem,
currentMode: currentMode,
intensity: intensity,
speed: speed,
isPlaying: isPlaying
})
}), jsx("div", {
className: cn("glass-relative glass-z-10"),
children: children
}), jsx(motion.div, {
initial: {
opacity: 0,
y: 20
},
animate: prefersReducedMotion ? {} : {
opacity: 1,
y: 0
},
className: cn("glass-absolute glass-top-4 glass-left-4 glass-px-3 glass-py-2 glass-radius-lg glass-foundation-complete glass-border glass-border-subtle glass-surface-dark"),
children: jsxs("div", {
className: cn("glass-flex glass-items-center glass-gap-2 glass-text-white"),
children: [jsx("div", {
className: cn("glass-flex glass-gap-1"),
children: getPaletteColors(colorPalette).map((color, index) => jsx("div", {
className: cn("glass-w-3 glass-h-3 glass-radius-full"),
style: {
backgroundColor: color
}
}, index))
}), jsx("span", {
className: cn("glass-text-sm glass-font-medium glass-capitalize"),
children: colorPalette
}), jsxs("div", {
className: cn("glass-flex glass-items-center glass-gap-1 glass-text-xs glass-text-secondary"),
children: [jsx(Flame, {
className: cn("glass-w-3 glass-h-3")
}), jsxs("span", {
children: [(intensity * 100).toFixed(0), "%"]
})]
})]
})
}), showControls && jsxs(motion.div, {
initial: {
opacity: 0,
y: 20
},
animate: prefersReducedMotion ? {} : {
opacity: 1,
y: 0
},
className: cn("glass-absolute glass-bottom-4 glass-right-4 glass-flex glass-flex-col glass-gap-2"),
children: [jsx("div", {
className: cn("glass-flex glass-gap-2 glass-p-2 glass-surface-dark glass-foundation-complete glass-radius-lg glass-border glass-border-subtle"),
children: ['flow', 'pulse', 'shift', 'mixed'].map(mode => jsx("button", {
onClick: () => changeAnimationMode(mode),
className: cn("glass-p-2 glass-radius-lg glass-transition-colors glass-focus glass-touch-target glass-contrast-guard", currentMode === mode ? "glass-surface-subtle glass-text-white" : "glass-text-secondary hover:glass-text-white hover:glass-surface-hover"),
title: `Switch to ${mode} animation`,
children: getAnimationIcon(mode)
}, mode))
}), jsxs("div", {
className: cn("glass-flex glass-gap-2 glass-p-2 glass-surface-dark glass-foundation-complete glass-radius-lg glass-border glass-border-subtle"),
children: [jsx("button", {
onClick: togglePlay,
className: cn("glass-p-2 glass-radius-lg glass-text-white hover:glass-surface-hover glass-transition-colors glass-focus glass-touch-target glass-contrast-guard"),
title: isPlaying ? 'Pause Aurora' : 'Play Aurora',
children: isPlaying ? jsx(Pause, {
className: cn("glass-w-4 glass-h-4")
}) : jsx(Play, {
className: cn("glass-w-4 glass-h-4")
})
}), jsxs("div", {
className: cn("glass-flex glass-items-center glass-gap-2 glass-text-secondary glass-text-sm"),
children: [jsx(Wind, {
className: cn("glass-w-3 glass-h-3")
}), jsxs("span", {
children: [speed.toFixed(1), "x"]
})]
})]
}), jsx("div", {
className: cn("glass-grid glass-grid-cols-2 glass-gap-2 glass-p-2 glass-surface-dark glass-foundation-complete glass-radius-lg glass-border glass-border-subtle"),
children: Object.keys(colorPalettes).slice(0, 6).map(palette => jsxs("button", {
onClick: () => {},
className: cn("glass-p-2 glass-radius-lg glass-transition-colors glass-text-xs glass-focus glass-touch-target glass-contrast-guard", colorPalette === palette ? "glass-surface-subtle glass-text-white" : "glass-text-secondary hover:glass-text-white hover:glass-surface-hover"),
title: `Switch to ${palette} palette`,
children: [jsx("div", {
className: cn("glass-flex glass-gap-0.5 glass-mb-1"),
children: getPaletteColors(palette).map((color, index) => jsx("div", {
className: cn("glass-w-2 glass-h-2 glass-radius-full"),
style: {
backgroundColor: color
}
}, index))
}), jsx("span", {
className: cn("glass-capitalize"),
children: palette
})]
}, palette))
})]
}), jsx(motion.div, {
initial: {
opacity: 0,
x: -20
},
animate: prefersReducedMotion ? {} : {
opacity: 1,
x: 0
},
className: cn("glass-absolute glass-top-4 glass-right-4 glass-px-3 glass-py-2 glass-radius-lg glass-foundation-complete glass-border glass-border-subtle glass-surface-dark"),
children: jsxs("div", {
className: cn("glass-flex glass-items-center glass-gap-2 glass-text-secondary glass-text-sm"),
children: [jsx(motion.div, {
animate: prefersReducedMotion ? {} : {
scale: isPlaying ? [1, 1.2, 1] : 1,
opacity: isPlaying ? [0.6, 1, 0.6] : 0.6
},
transition: prefersReducedMotion ? {
duration: 0
} : {
duration: 2,
repeat: isPlaying ? Infinity : 0,
ease: 'easeInOut'
},
children: jsx(Sparkles, {
className: cn("glass-w-3 glass-h-3")
})
}), jsx("span", {
className: cn("glass-capitalize"),
children: currentMode
}), isPlaying && jsx("div", {
className: cn("glass-w-2 glass-h-2 glass-surface-success glass-radius-full glass-animate-pulse")
})]
})
})]
});
}
// 3D Aurora Scene Component
function AuroraScene({
auroraMeshes,
particleSystem,
currentMode,
intensity,
speed,
isPlaying
}) {
const {
scene
} = useThree();
// Add aurora meshes to scene
useEffect(() => {
auroraMeshes.forEach(mesh => {
scene.add(mesh);
});
if (particleSystem) {
scene.add(particleSystem);
}
return () => {
auroraMeshes.forEach(mesh => {
scene.remove(mesh);
});
if (particleSystem) {
scene.remove(particleSystem);
}
};
}, [auroraMeshes, particleSystem, scene]);
// Animation loop
useFrame(state => {
if (!isPlaying) return;
const time = state.clock.elapsedTime;
// Animate aurora meshes
auroraMeshes.forEach((mesh, index) => {
const meshSpeed = speed * (0.8 + index * 0.2);
switch (currentMode) {
case 'flow':
AuroraAnimations.waveFlow(mesh, time, meshSpeed);
break;
case 'pulse':
AuroraAnimations.intensityPulse(mesh, time, meshSpeed);
break;
case 'shift':
AuroraAnimations.colorShift(mesh, time, meshSpeed);
break;
case 'mixed':
AuroraAnimations.waveFlow(mesh, time, meshSpeed);
AuroraAnimations.intensityPulse(mesh, time, meshSpeed * 0.5);
if (index % 2 === 0) {
AuroraAnimations.colorShift(mesh, time, meshSpeed * 0.3);
}
break;
}
});
// Animate particles
if (particleSystem) {
AuroraAnimations.particleDrift(particleSystem, time, speed * 0.5);
}
});
return jsxs(Fragment, {
children: [jsx("ambientLight", {
intensity: 0.1
}), jsx("directionalLight", {
position: [10, 10, 5],
intensity: 0.3,
color: 0x4fc3f7
}), jsx("pointLight", {
position: [0, 15, 0],
intensity: 0.2,
color: 0x81c784
}), jsx("fog", {
attach: "fog",
args: ['#000011', 10, 50]
})]
});
}
export { AuroraPro, AuroraPro as default };
//# sourceMappingURL=AuroraPro.js.map