UNPKG

aura-glass

Version:

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

264 lines (261 loc) 8.48 kB
import { useRef, useState, useCallback, useEffect } from 'react'; const DEFAULT_CONFIG = { strength: 0.3, range: 100, scale: 1.1, rotation: 5, easing: 'cubic-bezier(0.23, 1, 0.32, 1)', duration: 300, enabled: true, resetOnLeave: true }; function useMouseMagneticEffect(config = {}) { const elementRef = useRef(null); const [state, setState] = useState({ isActive: false, distance: 0, intensity: 0, transform: { x: 0, y: 0, scale: 1, rotation: 0 } }); const finalConfig = { ...DEFAULT_CONFIG, ...config }; const calculateMagneticEffect = useCallback((mouseX, mouseY, elementRect) => { const centerX = elementRect.left + elementRect.width / 2; const centerY = elementRect.top + elementRect.height / 2; const deltaX = mouseX - centerX; const deltaY = mouseY - centerY; const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance > finalConfig.range) { return { isActive: false, distance, intensity: 0, transform: { x: 0, y: 0, scale: 1, rotation: 0 } }; } const intensity = Math.max(0, 1 - distance / finalConfig.range); const strength = finalConfig.strength * intensity; const transform = { x: deltaX * strength, y: deltaY * strength, scale: 1 + (finalConfig.scale - 1) * intensity, rotation: deltaX / distance * finalConfig.rotation * intensity }; return { isActive: true, distance, intensity, transform }; }, [finalConfig]); const handleMouseMove = useCallback(event => { if (!elementRef.current || !finalConfig.enabled) return; const elementRect = elementRef.current.getBoundingClientRect(); const newState = calculateMagneticEffect(event.clientX, event.clientY, elementRect); setState(newState); // Apply transform directly to element if (elementRef.current) { const { transform } = newState; elementRef.current.style.transform = ` translate(${transform.x}px, ${transform.y}px) scale(${transform.scale}) rotate(${transform.rotation}deg) `; elementRef.current.style.transition = finalConfig.duration > 0 ? `transform ${finalConfig.duration}ms ${finalConfig.easing}` : 'none'; } }, [calculateMagneticEffect, finalConfig]); const handleMouseLeave = useCallback(() => { if (!finalConfig.resetOnLeave || !elementRef.current) return; setState({ isActive: false, distance: 0, intensity: 0, transform: { x: 0, y: 0, scale: 1, rotation: 0 } }); // Reset transform elementRef.current.style.transform = 'translate(0, 0) scale(1) rotate(0deg)'; }, [finalConfig.resetOnLeave]); const reset = useCallback(() => { if (!elementRef.current) return; setState({ isActive: false, distance: 0, intensity: 0, transform: { x: 0, y: 0, scale: 1, rotation: 0 } }); elementRef.current.style.transform = 'translate(0, 0) scale(1) rotate(0deg)'; }, []); useEffect(() => { const element = elementRef.current; if (!element || !finalConfig.enabled) return; // Add event listeners element.addEventListener('mousemove', handleMouseMove); element.addEventListener('mouseleave', handleMouseLeave); return () => { element.removeEventListener('mousemove', handleMouseMove); element.removeEventListener('mouseleave', handleMouseLeave); }; }, [handleMouseMove, handleMouseLeave, finalConfig.enabled]); return { ref: elementRef, state, reset, config: finalConfig }; } // Hook for applying magnetic effect to multiple elements function useMagneticField(config = {}) { const containerRef = useRef(null); const elementsRef = useRef(new Map()); const [activeElement, setActiveElement] = useState(null); const finalConfig = { ...DEFAULT_CONFIG, ...config }; const registerElement = useCallback((id, element) => { elementsRef.current.set(id, element); return () => { elementsRef.current.delete(id); }; }, []); const unregisterElement = useCallback(id => { elementsRef.current.delete(id); if (activeElement === id) { setActiveElement(null); } }, [activeElement]); const handleMouseMove = useCallback(event => { if (!finalConfig.enabled) return; let closestElement = null; let minDistance = Infinity; elementsRef.current.forEach((element, id) => { const rect = element.getBoundingClientRect(); const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; const distance = Math.sqrt(Math.pow(event.clientX - centerX, 2) + Math.pow(event.clientY - centerY, 2)); if (distance < minDistance && distance <= finalConfig.range) { minDistance = distance; closestElement = id; } }); if (closestElement !== activeElement) { // Reset previous active element if (activeElement && elementsRef.current.has(activeElement)) { const prevElement = elementsRef.current.get(activeElement); prevElement.style.transform = 'translate(0, 0) scale(1) rotate(0deg)'; } setActiveElement(closestElement); // Apply effect to new active element if (closestElement && elementsRef.current.has(closestElement)) { const element = elementsRef.current.get(closestElement); const rect = element.getBoundingClientRect(); const intensity = Math.max(0, 1 - minDistance / finalConfig.range); const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; const deltaX = event.clientX - centerX; const deltaY = event.clientY - centerY; const transform = { x: deltaX * finalConfig.strength * intensity, y: deltaY * finalConfig.strength * intensity, scale: 1 + (finalConfig.scale - 1) * intensity, rotation: deltaX / minDistance * finalConfig.rotation * intensity }; element.style.transform = ` translate(${transform.x}px, ${transform.y}px) scale(${transform.scale}) rotate(${transform.rotation}deg) `; element.style.transition = finalConfig.duration > 0 ? `transform ${finalConfig.duration}ms ${finalConfig.easing}` : 'none'; } } }, [activeElement, finalConfig]); const resetAll = useCallback(() => { elementsRef.current.forEach(element => { element.style.transform = 'translate(0, 0) scale(1) rotate(0deg)'; }); setActiveElement(null); }, []); useEffect(() => { const container = containerRef.current; if (!container || !finalConfig.enabled) return; container.addEventListener('mousemove', handleMouseMove); container.addEventListener('mouseleave', resetAll); return () => { container.removeEventListener('mousemove', handleMouseMove); container.removeEventListener('mouseleave', resetAll); }; }, [handleMouseMove, resetAll, finalConfig.enabled]); return { containerRef, registerElement, unregisterElement, activeElement, resetAll, config: finalConfig }; } // Utility hook for magnetic buttons function useMagneticButton(config = {}) { const { onClick, disabled, ...magneticConfig } = config; const magnetic = useMouseMagneticEffect(magneticConfig); const handleClick = useCallback(() => { if (!disabled && onClick) { // Add click feedback animation if (magnetic.ref.current) { magnetic.ref.current.style.animation = 'magnetic-click 0.2s ease-out'; setTimeout(() => { if (magnetic.ref.current) { magnetic.ref.current.style.animation = ''; } }, 200); } onClick(); } }, [disabled, onClick, magnetic.ref]); useEffect(() => { const element = magnetic.ref.current; if (!element) return; if (disabled) { element.style.pointerEvents = 'none'; element.style.opacity = '0.5'; } else { element.style.pointerEvents = 'auto'; element.style.opacity = '1'; } }, [disabled, magnetic.ref]); return { ...magnetic, handleClick, disabled: disabled || false }; } export { useMagneticButton, useMagneticField, useMouseMagneticEffect }; //# sourceMappingURL=useMouseMagneticEffect.js.map