aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
270 lines (267 loc) • 10.9 kB
JavaScript
'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