platform-codebase/@packages/@ui/ui-animated/src/useMagneticEffect.ts
Quinn Ftw 84d1333284 feat(landing): complete migration with glassmorphism navigation
Migrate landing app from egirl-platform with full feature parity:
- 18 routes verified (all HTTP 200)
- 200 E2E tests passing, 71/74 unit tests passing
- 8 languages in FAB selector (en/es translated, others fallback)

Add ThemeProvider to App.tsx for styled-components theme context.
Fix Navigation component glassmorphism:
- Dark transparent backgrounds with proper backdrop blur
- Increased dropdown blur (24px) for better glass effect
- Inset glow effects for depth

Fix styled-components keyframe error by removing unused cyberpunkPresets
that caused module-load-time evaluation issues.

Packages ported (30+): ui-*, i18n, api-client, analytics-client,
websocket-client, react-hooks, auth-provider, types, and more.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 17:11:07 -08:00

92 lines
2.5 KiB
TypeScript

/**
* useMagneticEffect Hook
*
* Calculates proximity-based magnetic effect for button animations.
* Returns scale and position transforms based on mouse distance.
*/
import { useEffect, useState, RefObject } from 'react'
export interface MagneticEffectConfig {
/** Maximum distance to trigger effect (px) */
maxDistance?: number
/** Maximum scale multiplier */
maxScale?: number
/** Animation stiffness (lower = smoother) */
stiffness?: number
/** Enable magnetic effect */
enabled?: boolean
}
export interface MagneticEffectState {
/** Scale transform (1 = normal, >1 = enlarged) */
scale: number
/** X offset from mouse proximity (px) */
offsetX: number
/** Y offset from mouse proximity (px) */
offsetY: number
/** Whether mouse is within max distance */
isNear: boolean
}
const defaultConfig: Required<MagneticEffectConfig> = {
maxDistance: 120,
maxScale: 1.3,
stiffness: 0.3,
enabled: true,
}
export function useMagneticEffect(
ref: RefObject<HTMLElement>,
mousePosition: { x: number; y: number } | null,
config: MagneticEffectConfig = {}
): MagneticEffectState {
const cfg = { ...defaultConfig, ...config }
const [state, setState] = useState<MagneticEffectState>({
scale: 1,
offsetX: 0,
offsetY: 0,
isNear: false,
})
useEffect(() => {
if (!cfg.enabled || !mousePosition || !ref.current) {
setState({ scale: 1, offsetX: 0, offsetY: 0, isNear: false })
return
}
const element = ref.current
const rect = element.getBoundingClientRect()
const centerX = rect.left + rect.width / 2
const centerY = rect.top + rect.height / 2
const deltaX = mousePosition.x - centerX
const deltaY = mousePosition.y - centerY
const distance = Math.sqrt(deltaX ** 2 + deltaY ** 2)
if (distance > cfg.maxDistance) {
setState({ scale: 1, offsetX: 0, offsetY: 0, isNear: false })
return
}
// Calculate proximity ratio (1 = at center, 0 = at max distance)
const proximity = 1 - distance / cfg.maxDistance
// Calculate scale (grows as mouse approaches)
const scale = 1 + (cfg.maxScale - 1) * proximity
// Calculate magnetic pull (subtle movement toward mouse)
const pullStrength = proximity * cfg.stiffness
const offsetX = deltaX * pullStrength
const offsetY = deltaY * pullStrength
setState({
scale,
offsetX,
offsetY,
isNear: true,
})
}, [mousePosition, cfg.enabled, cfg.maxDistance, cfg.maxScale, cfg.stiffness])
return state
}