UNPKG

aura-glass

Version:

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

931 lines (914 loc) 29.8 kB
'use client'; import { jsx, jsxs, Fragment } from 'react/jsx-runtime'; import { useReducedMotion } from '../../hooks/useReducedMotion.js'; import { OrbitControls, PerspectiveCamera } from '@react-three/drei'; import { Canvas, useThree, useFrame } from '@react-three/fiber'; import { motion } from 'framer-motion'; import { AlertCircle, Loader2, EyeOff, Eye, Info, Hand } from 'lucide-react'; import { useRef, useState, useEffect, useCallback, useMemo } from 'react'; import * as THREE from 'three'; import { cn } from '../../lib/utilsComprehensive.js'; // Mock WebXR hook for demonstration - in real implementation this would use actual WebXR APIs const useWebXR = () => { const [isSupported, setIsSupported] = useState(false); const [isARSupported, setIsARSupported] = useState(false); const [session, setSession] = useState({ isActive: false }); const [capabilities, setCapabilities] = useState({ supportsHandTracking: false, supportsHitTest: false, supportsDomOverlay: false, isARSupported: false }); const [handTracking, setHandTracking] = useState({ left: { isActive: false, position: new THREE.Vector3(), rotation: new THREE.Quaternion() }, right: { isActive: false, position: new THREE.Vector3(), rotation: new THREE.Quaternion() } }); const [hitTestResults, setHitTestResults] = useState([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); useEffect(() => { // Mock WebXR detection const checkSupport = async () => { // In real implementation, check for 'xr' in navigator const xrSupported = typeof navigator !== "undefined" && "xr" in navigator; const arSupported = xrSupported && (await navigator.xr?.isSessionSupported("immersive-ar")); setIsSupported(xrSupported); setIsARSupported(arSupported || false); setCapabilities({ supportsHandTracking: arSupported || false, supportsHitTest: arSupported || false, supportsDomOverlay: arSupported || false, isARSupported: arSupported || false }); }; checkSupport(); }, []); const startARSession = useCallback(async (options = {}) => { setIsLoading(true); try { // Mock AR session start await new Promise(resolve => setTimeout(resolve, 1000)); setSession({ isActive: true }); setIsLoading(false); } catch (err) { setError(err instanceof Error ? err.message : String(err)); setIsLoading(false); } }, []); const endARSession = useCallback(async () => { setSession({ isActive: false }); setHandTracking({ left: { isActive: false, position: new THREE.Vector3(), rotation: new THREE.Quaternion() }, right: { isActive: false, position: new THREE.Vector3(), rotation: new THREE.Quaternion() } }); }, []); const onHitTest = useCallback(callback => { // Mock hit test callback const mockResults = [{ point: new THREE.Vector3(0, 0, -2), distance: 2 }]; setHitTestResults(mockResults); callback(mockResults); }, []); const onHandTracking = useCallback(callback => { // Mock hand tracking callback const mockHands = { left: { isActive: true, position: new THREE.Vector3(-0.3, 1.2, -0.5), rotation: new THREE.Quaternion() }, right: { isActive: true, position: new THREE.Vector3(0.3, 1.2, -0.5), rotation: new THREE.Quaternion() } }; setHandTracking(mockHands); callback(mockHands); }, []); const startVoiceRecognition = useCallback(() => { // Mock voice recognition return { start: () => console.log("Voice recognition started"), stop: () => console.log("Voice recognition stopped") }; }, []); return { isSupported, isARSupported, session, capabilities, handTracking, hitTestResults, isLoading, error, startARSession, endARSession, onHitTest, onHandTracking, startVoiceRecognition }; }; // AR Glass Material Factory - creates glass materials optimized for AR const ARGlassMaterialFactory = { createSpatialUI: options => { return new THREE.ShaderMaterial({ uniforms: { time: { value: 0 }, userPosition: { value: new THREE.Vector3() }, interactionRadius: { value: options.interactionRadius || 3.0 }, glowIntensity: { value: options.glowIntensity || 1.0 }, color: { value: options.color || new THREE.Color(0.3, 0.7, 1.0) }, opacity: { value: options.opacity || 0.8 } }, vertexShader: ` varying vec3 vPosition; varying vec3 vNormal; varying vec3 vUserPosition; void main() { vPosition = position; vNormal = normal; vUserPosition = (modelViewMatrix * vec4(position, 1.0)).xyz; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` uniform float time; uniform vec3 userPosition; uniform float interactionRadius; uniform float glowIntensity; uniform vec3 color; uniform float opacity; varying vec3 vPosition; varying vec3 vNormal; varying vec3 vUserPosition; void main() { float distance = length(vUserPosition - userPosition); float interaction = 1.0 - smoothstep(0.0, interactionRadius, distance); vec3 finalColor = color + (glowIntensity * interaction * vec3(0.5, 0.8, 1.0)); float finalOpacity = opacity * (0.8 + 0.2 * interaction); gl_FragColor = vec4(finalColor, finalOpacity); } `, transparent: true, side: THREE.DoubleSide }); }, createHolographicGlass: options => { return new THREE.ShaderMaterial({ uniforms: { time: { value: 0 }, color: { value: options.color || new THREE.Color(0.6, 0.8, 1.0) }, opacity: { value: options.opacity || 0.9 }, fresnelPower: { value: options.fresnelPower || 1.5 } }, vertexShader: ` varying vec3 vNormal; varying vec3 vViewDirection; void main() { vNormal = normalize(normalMatrix * normal); vec4 mvPosition = modelViewMatrix * vec4(position, 1.0); vViewDirection = normalize(-mvPosition.xyz); gl_Position = projectionMatrix * mvPosition; } `, fragmentShader: ` uniform float time; uniform vec3 color; uniform float opacity; uniform float fresnelPower; varying vec3 vNormal; varying vec3 vViewDirection; void main() { float fresnel = pow(1.0 - dot(vNormal, vViewDirection), fresnelPower); vec3 finalColor = color * (0.5 + 0.5 * fresnel); float finalOpacity = opacity * (0.7 + 0.3 * fresnel); gl_FragColor = vec4(finalColor, finalOpacity); } `, transparent: true, side: THREE.DoubleSide }); }, createDataVisualization: options => { return new THREE.ShaderMaterial({ uniforms: { time: { value: 0 }, lowColor: { value: options.lowColor || new THREE.Color(0.2, 0.4, 1.0) }, highColor: { value: options.highColor || new THREE.Color(1.0, 0.4, 0.2) }, dataValue: { value: options.dataValue || 0.5 } }, vertexShader: ` varying vec3 vPosition; void main() { vPosition = position; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` uniform float time; uniform vec3 lowColor; uniform vec3 highColor; uniform float dataValue; varying vec3 vPosition; void main() { float height = vPosition.y; vec3 finalColor = mix(lowColor, highColor, dataValue); // Add some animation float pulse = sin(time * 2.0) * 0.1 + 0.9; finalColor *= pulse; gl_FragColor = vec4(finalColor, 0.8); } `, transparent: true }); } }; // AR Glass Geometry Factory - creates optimized geometries for AR const ARGlassGeometryFactory = { createCurvedPanel: (width, height, depth) => { const geometry = new THREE.PlaneGeometry(width, height, 32, 32); // Add some curvature for more interesting glass effect const positions = geometry.attributes.position.array; for (let i = 0; i < positions.length; i += 3) { const x = positions[i]; const y = positions[i + 1]; positions[i + 2] = Math.sin(x * 0.5) * Math.cos(y * 0.5) * depth; } geometry.computeVertexNormals(); return geometry; }, createPortalGeometry: (innerRadius, outerRadius) => { const geometry = new THREE.RingGeometry(innerRadius, outerRadius, 32); // Add some depth variation const positions = geometry.attributes.position.array; for (let i = 0; i < positions.length; i += 3) { const angle = Math.atan2(positions[i + 1], positions[i]); positions[i + 2] = Math.sin(angle * 4) * 0.1; } geometry.computeVertexNormals(); return geometry; }, createParticleField: count => { const geometry = new THREE.BufferGeometry(); const positions = new Float32Array(count * 3); const colors = new Float32Array(count * 3); for (let i = 0; i < count; i++) { positions[i * 3] = (Math.random() - 0.5) * 10; positions[i * 3 + 1] = (Math.random() - 0.5) * 10; positions[i * 3 + 2] = (Math.random() - 0.5) * 10; colors[i * 3] = Math.random(); colors[i * 3 + 1] = Math.random(); colors[i * 3 + 2] = Math.random(); } geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3)); geometry.setAttribute("color", new THREE.BufferAttribute(colors, 3)); return geometry; } }; // AR Glass Animations - provides smooth animations for AR elements const ARGlassAnimations = { createFloatingAnimation: (object, amplitude = 0.05, frequency = 0.5) => { return time => { object.position.y += Math.sin(time * frequency) * amplitude; }; }, createRotationAnimation: (object, axis, speed = 1) => { return time => { object.rotateOnAxis(axis, speed * 0.01); }; }, createScaleAnimation: (object, minScale, maxScale, frequency = 2) => { return time => { const scale = minScale + (maxScale - minScale) * (Math.sin(time * frequency) * 0.5 + 0.5); object.scale.setScalar(scale); }; } }; // AR Glass Interactions - handles user interactions in AR const ARGlassInteractions = { createHapticFeedback: (intensity = 0.5, duration = 100) => { if ("vibrate" in navigator) { navigator.vibrate(duration * intensity); } }, setupHandTracking: (objects, callback) => { // Mock hand tracking setup const handleHandMove = handData => { objects.forEach((obj, index) => { if (obj && handData.left.isActive) { const distance = obj.position.distanceTo(handData.left.position); if (distance < 0.5) { callback(obj, handData.left); } } }); }; return handleHandMove; } }; // AR Glass Utils - utility functions for AR glass effects const ARGlassUtils = { createAdaptiveScaling: (minScale, maxScale) => { return distance => { const normalizedDistance = Math.min(Math.max(distance, 1), 10); const scale = maxScale - (normalizedDistance - 1) / 9 * (maxScale - minScale); return Math.max(scale, minScale); }; }, createDistanceBasedOpacity: (maxDistance = 5) => { return distance => { return Math.max(0, 1 - distance / maxDistance); }; } }; function ARGlassEffects({ mode = "preview", content = {}, onInteraction, className = "", enablePhysics = false, enableHandTracking = false, enableVoiceControl = false, adaptiveScaling = true, showControls = true, showInfo = true }) { const prefersReducedMotion = useReducedMotion(); const canvasRef = useRef(null); const [userPosition] = useState(new THREE.Vector3(0, 1.6, 0)); const [isInitialized, setIsInitialized] = useState(false); const [error, setError] = useState(null); const [portalActive, setPortalActive] = useState(false); const { capabilities, session, handTracking, hitTestResults, isLoading, error: xrError, startARSession, endARSession, onHitTest, onHandTracking, startVoiceRecognition } = useWebXR(); // Initialize AR Glass Effects useEffect(() => { const initialize = async () => { try { setIsInitialized(true); } catch (error) { setError(`Failed to initialize AR Glass Effects: ${error}`); } }; initialize(); }, []); // Setup hand tracking useEffect(() => { if (enableHandTracking && capabilities.supportsHandTracking) { const handleHandTracking = ARGlassInteractions.setupHandTracking([], // Objects will be populated from scene (object, hand) => { if (onInteraction) { onInteraction("hand_interaction", { object: object.uuid, hand }); } }); onHandTracking(handleHandTracking); } }, [enableHandTracking, capabilities.supportsHandTracking, onHandTracking, onInteraction]); // Setup voice control useEffect(() => { if (enableVoiceControl && mode === "ar") { const recognition = startVoiceRecognition(); return () => { if (recognition) { recognition.stop(); } }; } }, [enableVoiceControl, mode, startVoiceRecognition]); // Handle AR session start/stop const handleARToggle = useCallback(async () => { if (session.isActive) { await endARSession(); } else { try { await startARSession({ requiredFeatures: ["local-floor"], optionalFeatures: enableHandTracking ? ["hand-tracking"] : [], domOverlay: canvasRef.current?.parentElement ? { root: canvasRef.current.parentElement } : undefined }); } catch (error) { setError(`Failed to start AR session: ${error}`); } } }, [session.isActive, endARSession, startARSession, enableHandTracking]); // Handle hit test results useEffect(() => { onHitTest(results => { if (onInteraction && results.length > 0) { onInteraction("surface_detected", { points: results.map(r => r.point), count: results.length }); } }); }, [onHitTest, onInteraction]); const handlePanelInteraction = useCallback(panelType => { if (onInteraction) { onInteraction("panel_interaction", { type: panelType }); } }, [onInteraction]); const handlePortalActivation = useCallback(() => { useReducedMotion(); setPortalActive(!portalActive); if (onInteraction) { onInteraction("portal_toggle", { active: !portalActive }); } }, [portalActive, onInteraction]); if (error) { return jsx("div", { className: cn("ar-glass-error glass-foundation-complete glass-surface-danger", "glass-p-md glass-radius-lg glass-text-danger", className), children: jsxs("div", { className: "glass-flex glass-items-center glass-gap-2", children: [jsx(AlertCircle, { className: 'w-5 h-5' }), jsxs("span", { children: ["AR Glass Effects Error: ", error] })] }) }); } if (!isInitialized) { return jsx("div", { className: cn("ar-glass-loading glass-foundation-complete glass-surface-info", "glass-p-md glass-radius-lg glass-text-info", className), children: jsxs("div", { className: "glass-flex glass-items-center glass-gap-2", children: [jsx(Loader2, { className: 'w-5 h-5 animate-spin' }), jsx("span", { children: "Initializing AR Glass Effects..." })] }) }); } return jsxs("div", { className: cn("ar-glass-effects glass-foundation-complete relative", className), children: [mode === "ar" && capabilities.isARSupported && showControls && jsxs(motion.div, { initial: { opacity: 0, y: -20 }, animate: prefersReducedMotion ? {} : { opacity: 1, y: 0 }, className: 'absolute glass-top-4 glass-right-4 z-10 glass-flex glass-gap-2', children: [jsxs("button", { onClick: handleARToggle, disabled: isLoading, className: cn("glass-foundation-complete glass-surface-primary glass-text-primary", "glass-px-md glass-py-sm glass-radius-lg glass-shadow-lg", "hover:glass-surface-primary-hover disabled:opacity-50", "flex items-center glass-gap-2 glass-transition glass-focus glass-touch-target glass-contrast-guard", "glass-press glass-magnet"), children: [isLoading ? jsx(Loader2, { className: 'w-4 h-4 animate-spin glass-touch-target glass-contrast-guard' }) : session.isActive ? jsx(EyeOff, { className: 'w-4 h-4' }) : jsx(Eye, { className: 'w-4 h-4' }), isLoading ? "Loading..." : session.isActive ? "Exit AR" : "Enter AR"] }), enableVoiceControl && jsx("div", { className: 'glass-px-3 glass-py-2 glass-surface-green/20 text-primary glass-radius-lg glass-text-sm glass-contrast-guard', children: "\uD83C\uDFA4 Voice Active" })] }), mode === "demo" && showInfo && jsxs(motion.div, { initial: { opacity: 0, x: -20 }, animate: prefersReducedMotion ? {} : { opacity: 1, x: 0 }, className: 'absolute top-4 left-4 z-10 glass-p-4 glass-surface-dark/80 glass-glass-glass-backdrop-blur-lg glass-radius-lg text-primary glass-text-sm max-w-xs glass-contrast-guard', children: [jsxs("h3", { className: 'font-semibold mb-2 glass-flex glass-items-center glass-gap-2', children: [jsx(Info, { className: 'w-4 h-4' }), "AR Capabilities"] }), jsxs("ul", { className: 'space-y-1', children: [jsxs("li", { className: "glass-flex glass-items-center glass-gap-2", children: [jsx("span", { className: capabilities.isARSupported ? "text-green-400" : "text-red-400", children: capabilities.isARSupported ? "✅" : "❌" }), "AR Supported"] }), jsxs("li", { className: "glass-flex glass-items-center glass-gap-2", children: [jsx("span", { className: capabilities.supportsHandTracking ? "text-green-400" : "text-red-400", children: capabilities.supportsHandTracking ? "✅" : "❌" }), "Hand Tracking"] }), jsxs("li", { className: "glass-flex glass-items-center glass-gap-2", children: [jsx("span", { className: capabilities.supportsHitTest ? "text-green-400" : "text-red-400", children: capabilities.supportsHitTest ? "✅" : "❌" }), "Hit Testing"] }), jsxs("li", { className: "glass-flex glass-items-center glass-gap-2", children: [jsx("span", { className: capabilities.supportsDomOverlay ? "text-green-400" : "text-red-400", children: capabilities.supportsDomOverlay ? "✅" : "❌" }), "DOM Overlay"] })] })] }), xrError && jsx(motion.div, { initial: { opacity: 0, y: 20 }, animate: prefersReducedMotion ? {} : { opacity: 1, y: 0 }, className: 'absolute bottom-4 left-4 z-10 glass-surface-red text-primary glass-p-3 glass-radius-lg max-w-xs glass-contrast-guard', children: jsxs("div", { className: "glass-flex glass-items-center glass-gap-2", children: [jsx(AlertCircle, { className: 'w-4 h-4' }), jsx("span", { className: "glass-text-sm", children: xrError })] }) }), enableHandTracking && (handTracking.left.isActive || handTracking.right.isActive) && jsx(motion.div, { initial: { opacity: 0, y: 20 }, animate: prefersReducedMotion ? {} : { opacity: 1, y: 0 }, className: 'absolute bottom-4 right-4 z-10 glass-surface-green text-primary glass-p-3 glass-radius-lg glass-text-sm glass-contrast-guard', children: jsxs("div", { className: "glass-flex glass-items-center glass-gap-2", children: [jsx(Hand, { className: 'w-4 h-4' }), jsxs("span", { children: ["Hands: ", handTracking.left.isActive ? "L" : "", " ", handTracking.right.isActive ? "R" : ""] })] }) }), jsxs(Canvas, { ref: canvasRef, className: "glass-w-full glass-h-full", camera: { position: [0, 1.6, 3], fov: 75 }, gl: { antialias: true, alpha: true, preserveDrawingBuffer: true }, children: [mode !== "ar" && jsx(OrbitControls, { enablePan: true, enableZoom: true, enableRotate: true }), jsx(PerspectiveCamera, { makeDefault: true, position: [0, 1.6, 3] }), jsx(ARScene, { content: content, onInteraction: onInteraction, userPosition: userPosition, enablePhysics: enablePhysics, portalActive: portalActive, onPortalActivation: handlePortalActivation, onPanelInteraction: handlePanelInteraction })] })] }); } // AR Scene Component function ARScene({ content, onInteraction, userPosition, enablePhysics, portalActive, onPortalActivation, onPanelInteraction }) { const { scene } = useThree(); // Initialize physics if enabled useEffect(() => { if (enablePhysics) { scene.traverse(child => { if (child instanceof THREE.Mesh && child.userData.physics) { // Add physics objects to the system console.log("Physics object added:", child.uuid); } }); } }, [scene, enablePhysics]); // Update physics useFrame((state, delta) => { }); return jsxs(Fragment, { children: [jsx("ambientLight", { intensity: 0.4 }), jsx("pointLight", { position: [10, 10, 10], intensity: 0.8 }), jsx("directionalLight", { position: [0, 10, 5], intensity: 0.5 }), jsx(FloatingPanel, { position: [0, 1.5, -2], content: content?.title || "AR Content", userPosition: userPosition, onInteraction: () => onPanelInteraction("main") }), jsx(FloatingPanel, { position: [-2, 1, -2.5], rotation: [0, 0.3, 0], content: content?.text || "Additional Information", userPosition: userPosition, onInteraction: () => onPanelInteraction("secondary") }), content?.data && jsx(DataVisualization, { data: content.data, position: [2, 0.5, -2], userPosition: userPosition }), jsx(HolographicPortal, { position: [0, 0, -5], isActive: portalActive, onActivate: onPortalActivation }), jsx(ParticleField, { position: [0, 0, -3], count: 50 }), jsx("group", { position: [0, -1, 0], children: jsxs("mesh", { rotation: [-Math.PI / 2, 0, 0], children: [jsx("planeGeometry", { args: [20, 20] }), jsx("meshBasicMaterial", { color: 0x333333, transparent: true, opacity: 0.1 })] }) })] }); } // Floating Panel Component function FloatingPanel({ position, rotation = [0, 0, 0], content, userPosition, onInteraction, isInteractive = true }) { const meshRef = useRef(null); const materialRef = useRef(null); const [isHovered, setIsHovered] = useState(false); const [distance, setDistance] = useState(0); const geometry = useMemo(() => ARGlassGeometryFactory.createCurvedPanel(2, 1.5, 0.2), []); const material = useMemo(() => ARGlassMaterialFactory.createSpatialUI({ color: new THREE.Color(0.3, 0.7, 1.0), opacity: 0.8, userPosition, interactionRadius: 3.0, glowIntensity: isHovered ? 2.0 : 1.0 }), [userPosition, isHovered]); useFrame(state => { if (!meshRef.current || !materialRef.current) return; const time = state.clock.elapsedTime; const mesh = meshRef.current; const mat = materialRef.current; // Update distance to user const currentDistance = mesh.position.distanceTo(userPosition); setDistance(currentDistance); // Update material uniforms mat.uniforms.time.value = time; mat.uniforms.userPosition.value.copy(userPosition); // Floating animation const floatingAnimation = ARGlassAnimations.createFloatingAnimation(mesh, 0.05, 0.5); floatingAnimation(time); // Adaptive scaling if (isInteractive) { const scale = ARGlassUtils.createAdaptiveScaling(1.0, 5.0)(currentDistance); mesh.scale.setScalar(scale); } // Look at user mesh.lookAt(userPosition); }); const handlePointerEnter = useCallback(() => { setIsHovered(true); }, []); const handlePointerLeave = useCallback(() => { setIsHovered(false); }, []); const handleClick = useCallback(() => { if (onInteraction) { onInteraction(); ARGlassInteractions.createHapticFeedback(0.7, 150); } }, [onInteraction]); return jsx("mesh", { ref: meshRef, position: position, rotation: rotation, geometry: geometry, material: material, onPointerEnter: handlePointerEnter, onPointerLeave: handlePointerLeave, onClick: handleClick }); } // Data Visualization Component function DataVisualization({ data, position, userPosition }) { const groupRef = useRef(null); const materials = useRef([]); const geometries = useMemo(() => { return data.map((_, index) => { const height = data[index] * 2; return new THREE.BoxGeometry(0.2, height, 0.2); }); }, [data]); const dataMaterials = useMemo(() => { return data.map(value => ARGlassMaterialFactory.createDataVisualization({ lowColor: new THREE.Color(0.2, 0.4, 1.0), highColor: new THREE.Color(1.0, 0.4, 0.2), opacity: 0.8, dataValue: value })); }, [data]); useFrame(state => { if (!groupRef.current) return; const time = state.clock.elapsedTime; // Update materials materials.current.forEach((material, index) => { if (material && material.uniforms) { material.uniforms.time.value = time; material.uniforms.animationProgress.value = Math.min(1, time * 0.5); material.uniforms.dataValue.value = data[index]; } }); // Look at user groupRef.current.lookAt(userPosition); }); return jsx("group", { ref: groupRef, position: position, children: data.map((value, index) => jsx("mesh", { position: [(index - data.length / 2) * 0.3, value, 0], geometry: geometries[index], material: dataMaterials[index], ref: ref => { if (ref) materials.current[index] = ref.material; } }, index)) }); } // Holographic Portal Component function HolographicPortal({ position, isActive, onActivate }) { const meshRef = useRef(null); const materialRef = useRef(null); const geometry = useMemo(() => ARGlassGeometryFactory.createPortalGeometry(0.8, 1.2), []); const material = useMemo(() => ARGlassMaterialFactory.createHolographicGlass({ color: new THREE.Color(0.6, 0.8, 1.0), opacity: isActive ? 0.9 : 0.5, fresnelPower: 1.5 }), [isActive]); useFrame(state => { if (!meshRef.current || !materialRef.current) return; const time = state.clock.elapsedTime; const mesh = meshRef.current; const mat = materialRef.current; // Update shader uniforms mat.uniforms.time.value = time; mat.uniforms.opacity.value = isActive ? 0.9 : 0.5; // Rotation animation const rotationAnimation = ARGlassAnimations.createRotationAnimation(mesh, new THREE.Vector3(0, 1, 0), isActive ? 2 : 0.5); rotationAnimation(time); // Scale animation when active if (isActive) { const scaleAnimation = ARGlassAnimations.createScaleAnimation(mesh, 1.0, 1.1, 3); scaleAnimation(time); } }); return jsx("mesh", { ref: meshRef, position: position, geometry: geometry, material: material, onClick: onActivate }); } // Particle Field Component function ParticleField({ position, count }) { const meshRef = useRef(null); const geometry = useMemo(() => ARGlassGeometryFactory.createParticleField(count), [count]); const material = useMemo(() => new THREE.PointsMaterial({ size: 0.05, vertexColors: true, transparent: true, opacity: 0.6, blending: THREE.AdditiveBlending }), []); useFrame(state => { if (!meshRef.current) return; const time = state.clock.elapsedTime; const positions = geometry.attributes.position.array; // Animate particles for (let i = 0; i < positions.length; i += 3) { positions[i + 1] += Math.sin(time + i * 0.01) * 0.002; } geometry.attributes.position.needsUpdate = true; }); return jsx("points", { ref: meshRef, position: position, geometry: geometry, material: material }); } export { ARGlassAnimations, ARGlassEffects, ARGlassGeometryFactory, ARGlassInteractions, ARGlassMaterialFactory, ARGlassUtils, ARGlassEffects as default }; //# sourceMappingURL=ARGlassEffects.js.map