UNPKG

aura-glass

Version:

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

270 lines (267 loc) 10.9 kB
'use client'; import { jsx, jsxs } from 'react/jsx-runtime'; import { useReducedMotion } from '../../hooks/useReducedMotion.js'; import { useRef, useState, useEffect, useCallback } from 'react'; import { useMotionValue, useSpring, useTransform, motion } from 'framer-motion'; import { cn } from '../../lib/utilsComprehensive.js'; function GlassOrientationEffects({ children, className, sensitivity = 0.7, smoothing = 0.8, effectTypes = ["tilt", "parallax", "refraction"], gyroscopeEnabled = true, accelerometerEnabled = true, compassEnabled = true, onOrientationChange, onMotionChange }) { useReducedMotion(); const containerRef = useRef(null); const [isSupported, setIsSupported] = useState(false); const [permissionGranted, setPermissionGranted] = useState(false); const [orientation, setOrientation] = useState({ alpha: 0, beta: 0, gamma: 0, absolute: false }); const [motionData, setMotionData] = useState({ acceleration: null, accelerationIncludingGravity: null, rotationRate: null, interval: 0 }); // Motion values const tiltX = useMotionValue(0); const tiltY = useMotionValue(0); const tiltZ = useMotionValue(0); const parallaxX = useMotionValue(0); const parallaxY = useMotionValue(0); const refractionX = useMotionValue(0); const refractionY = useMotionValue(0); const shimmerAngle = useMotionValue(0); // Smooth springs const springConfig = { stiffness: 100 * (1 - smoothing), damping: 20 * smoothing }; const smoothTiltX = useSpring(tiltX, springConfig); const smoothTiltY = useSpring(tiltY, springConfig); const smoothTiltZ = useSpring(tiltZ, springConfig); const smoothParallaxX = useSpring(parallaxX, springConfig); const smoothParallaxY = useSpring(parallaxY, springConfig); useSpring(refractionX, springConfig); useSpring(refractionY, springConfig); useSpring(shimmerAngle, springConfig); // Transform ranges const perspectiveX = useTransform(smoothTiltY, [-90, 90], [-30, 30]); const perspectiveY = useTransform(smoothTiltX, [-180, 180], [-20, 20]); const backgroundX = useTransform(smoothParallaxX, [-1, 1], [-50, 50]); const backgroundY = useTransform(smoothParallaxY, [-1, 1], [-50, 50]); // Check for device orientation support useEffect(() => { const checkSupport = () => { const hasOrientation = "DeviceOrientationEvent" in window; const hasMotion = "DeviceMotionEvent" in window; setIsSupported(hasOrientation || hasMotion); }; checkSupport(); }, []); // Request permissions for iOS 13+ const requestPermissions = useCallback(async () => { if (typeof DeviceOrientationEvent.requestPermission === "function") { try { const orientationPermission = await DeviceOrientationEvent.requestPermission(); const motionPermission = await DeviceMotionEvent.requestPermission(); setPermissionGranted(orientationPermission === "granted" || motionPermission === "granted"); } catch (error) { console.warn("Error requesting device orientation permission:", error); setPermissionGranted(false); } } else { // Assume permission granted on non-iOS devices setPermissionGranted(true); } }, []); // Handle device orientation changes const handleOrientationChange = useCallback(event => { if (!gyroscopeEnabled && !compassEnabled) return; const orientationData = { alpha: event.alpha || 0, beta: event.beta || 0, gamma: event.gamma || 0, absolute: event.absolute || false }; setOrientation(orientationData); onOrientationChange?.(orientationData); // Apply effects based on orientation if (effectTypes.includes("tilt")) { tiltX.set(orientationData.beta * sensitivity); tiltY.set(orientationData.gamma * sensitivity); tiltZ.set(orientationData.alpha * sensitivity * 0.1); } if (effectTypes.includes("parallax")) { parallaxX.set(orientationData.gamma / 90 * sensitivity); parallaxY.set(orientationData.beta / 180 * sensitivity); } if (effectTypes.includes("refraction")) { refractionX.set(orientationData.gamma / 90 * sensitivity * 0.5); refractionY.set(orientationData.beta / 180 * sensitivity * 0.5); } if (effectTypes.includes("shimmer")) { shimmerAngle.set(orientationData.alpha); } }, [gyroscopeEnabled, compassEnabled, sensitivity, effectTypes, onOrientationChange]); // Handle device motion changes const handleMotionChange = useCallback(event => { if (!accelerometerEnabled) return; const motionData = { acceleration: event.acceleration && event.acceleration.x !== null && event.acceleration.y !== null && event.acceleration.z !== null ? { x: event.acceleration.x, y: event.acceleration.y, z: event.acceleration.z } : null, accelerationIncludingGravity: event.accelerationIncludingGravity && event.accelerationIncludingGravity.x !== null && event.accelerationIncludingGravity.y !== null && event.accelerationIncludingGravity.z !== null ? { x: event.accelerationIncludingGravity.x, y: event.accelerationIncludingGravity.y, z: event.accelerationIncludingGravity.z } : null, rotationRate: event.rotationRate && event.rotationRate.alpha !== null && event.rotationRate.beta !== null && event.rotationRate.gamma !== null ? { alpha: event.rotationRate.alpha, beta: event.rotationRate.beta, gamma: event.rotationRate.gamma } : null, interval: event.interval }; setMotionData(motionData); onMotionChange?.(motionData); // Apply liquid effect based on acceleration if (effectTypes.includes("liquid") && motionData.acceleration) { const { x, y, z } = motionData.acceleration; const intensity = Math.sqrt(x * x + y * y + z * z) / 10; // Normalize tiltX.set(tiltX.get() + y * intensity * sensitivity); tiltY.set(tiltY.get() + x * intensity * sensitivity); } }, [accelerometerEnabled, sensitivity, effectTypes, onMotionChange]); // Setup event listeners useEffect(() => { if (!isSupported || !permissionGranted) return; if (gyroscopeEnabled || compassEnabled) { window.addEventListener("deviceorientation", handleOrientationChange); } if (accelerometerEnabled) { window.addEventListener("devicemotion", handleMotionChange); } return () => { window.removeEventListener("deviceorientation", handleOrientationChange); window.removeEventListener("devicemotion", handleMotionChange); }; }, [isSupported, permissionGranted, gyroscopeEnabled, compassEnabled, accelerometerEnabled, handleOrientationChange, handleMotionChange]); // Handle orientation change (screen rotation) useEffect(() => { const handleScreenOrientationChange = () => { // Reset motion values on orientation change tiltX.set(0); tiltY.set(0); tiltZ.set(0); parallaxX.set(0); parallaxY.set(0); }; window.addEventListener("orientationchange", handleScreenOrientationChange); window.addEventListener("resize", handleScreenOrientationChange); return () => { window.removeEventListener("orientationchange", handleScreenOrientationChange); window.removeEventListener("resize", handleScreenOrientationChange); }; }, []); // Permission request component if (isSupported && !permissionGranted) { return jsx("div", { className: cn("relative OptimizedGlass intensity={0.2} glassBlur={6} glass-p-6", className), children: jsxs("div", { className: 'text-center', children: [jsx("h3", { className: 'glass-text-lg font-semibold mb-2', children: "Enable Device Orientation" }), jsx("p", { className: 'glass-text-secondary mb-4', children: "Allow orientation access for interactive glass effects" }), jsx("button", { onClick: requestPermissions, className: "glass-button glass-button-primary glass-focus glass-touch-target glass-contrast-guard glass-focus glass-touch-target glass-contrast-guard", children: "Enable Orientation Effects" })] }) }); } if (!isSupported) { return jsx("div", { className: cn("relative", className), children: children }); } return jsxs(motion.div, { ref: containerRef, className: cn("relative OptimizedGlass intensity={0.2} glassBlur={6} overflow-hidden", "transform-gpu will-change-transform", className), style: { transformStyle: "preserve-3d", perspective: "1000px" }, children: [effectTypes.includes("tilt") && jsxs(motion.div, { className: 'absolute inset-0', style: { rotateX: perspectiveX, rotateY: perspectiveY, rotateZ: smoothTiltZ }, children: [effectTypes.includes("parallax") && jsx(motion.div, { className: 'absolute inset-0 OptimizedGlass intensity={0.2} glassBlur={6} opacity-30', style: { x: backgroundX, y: backgroundY, scale: 1.1 } }), effectTypes.includes("refraction") && jsx(motion.div, { className: "glass-absolute glass-inset-0 glass-pointer-events-none glass-surface-primary/10" }), effectTypes.includes("shimmer") && jsx(motion.div, { className: 'glass-absolute glass-inset-0 glass-pointer-events-none opacity-20 glass-surface-primary/10' }), jsx("div", { className: 'relative z-10', children: children })] }), !effectTypes.includes("tilt") && jsxs("div", { className: 'relative', children: [effectTypes.includes("parallax") && jsx(motion.div, { className: "glass-absolute glass-inset-0 glass-opacity-30", style: { x: backgroundX, y: backgroundY, scale: 1.1 } }), effectTypes.includes("refraction") && jsx(motion.div, { className: "glass-absolute glass-inset-0 glass-pointer-events-none glass-surface-primary/10" }), effectTypes.includes("shimmer") && jsx(motion.div, { className: 'glass-absolute glass-inset-0 glass-pointer-events-none opacity-20 glass-surface-primary/10' }), jsx("div", { className: 'relative z-10', children: children })] }), process.env.NODE_ENV === "development" && jsxs("div", { className: 'absolute bottom-2 left-2 glass-text-xs glass-surface-primary glass-p-2 glass-radius-sm opacity-50', children: [jsxs("div", { children: ["\u03B1: ", orientation.alpha.toFixed(1), "\u00B0"] }), jsxs("div", { children: ["\u03B2: ", orientation.beta.toFixed(1), "\u00B0"] }), jsxs("div", { children: ["\u03B3: ", orientation.gamma.toFixed(1), "\u00B0"] })] })] }); } export { GlassOrientationEffects }; //# sourceMappingURL=GlassOrientationEffects.js.map