UNPKG

aura-glass

Version:

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

412 lines (409 loc) 12.7 kB
'use client'; import { useRef, useState, useEffect, useCallback } from 'react'; import { GalileoPhysicsSystem } from '../../animations/physics/galileoPhysicsSystem.js'; const DEFAULT_CONFIG = { stiffness: 100, damping: 10, mass: 1, precision: 0.01, velocity: 0, gravity: false, gravityStrength: 9.81, enableCollisions: false, bounds: { left: -Infinity, right: Infinity, top: -Infinity, bottom: Infinity } }; function useGalileoSprings(targets = [], config = {}) { const finalConfig = { ...DEFAULT_CONFIG, ...config }; const physicsSystemRef = useRef(); const [positions, setPositions] = useState({}); const [isAnimating, setIsAnimating] = useState(false); // Initialize physics system useEffect(() => { physicsSystemRef.current = new GalileoPhysicsSystem({ gravity: finalConfig.gravity ? { x: 0, y: finalConfig.gravityStrength } : { x: 0, y: 0 }, bounds: finalConfig.bounds }); // Add initial bodies targets.forEach(target => { physicsSystemRef.current.addBody({ id: target.id, position: { ...target.position }, velocity: target.velocity || { x: 0, y: 0 }, acceleration: { x: 0, y: 0 }, mass: target.mass || finalConfig.mass, radius: target.radius || 10, friction: 0.1, restitution: 0.8, fixed: target.fixed || false, forces: [] }); }); physicsSystemRef.current.start(); return () => { physicsSystemRef.current?.stop(); }; }, [targets, finalConfig]); // Update positions when physics system updates useEffect(() => { if (!physicsSystemRef.current) return; const updatePositions = () => { const bodies = physicsSystemRef.current.getAllBodies(); const newPositions = {}; bodies.forEach(body => { newPositions[body.id] = { ...body.position }; }); setPositions(newPositions); // Check if any bodies are still moving const hasMovingBodies = bodies.some(body => Math.abs(body.velocity.x) > finalConfig.precision || Math.abs(body.velocity.y) > finalConfig.precision); setIsAnimating(hasMovingBodies); if (hasMovingBodies) { requestAnimationFrame(updatePositions); } }; updatePositions(); }, [finalConfig.precision]); // Apply spring force to move body to target const springTo = useCallback((id, target, customConfig) => { if (!physicsSystemRef.current) return; const body = physicsSystemRef.current.getBody(id); if (!body) return; const springConfig = { ...finalConfig, ...customConfig }; // Calculate spring force const dx = target.x - body.position.x; const dy = target.y - body.position.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance > springConfig.precision) { const force = { x: dx * springConfig.stiffness - body.velocity.x * springConfig.damping, y: dy * springConfig.stiffness - body.velocity.y * springConfig.damping }; physicsSystemRef.current.applyForce(id, force, 1 / 60); // Apply for one frame } }, [finalConfig]); // Apply impulse to body const impulse = useCallback((id, impulse) => { if (!physicsSystemRef.current) return; physicsSystemRef.current.applyImpulse(id, impulse); }, []); // Set body position directly const setPosition = useCallback((id, position) => { if (!physicsSystemRef.current) return; const body = physicsSystemRef.current.getBody(id); if (body) { body.position = { ...position }; body.velocity = { x: 0, y: 0 }; body.acceleration = { x: 0, y: 0 }; } }, []); // Add new body to physics system const addBody = useCallback(body => { if (!physicsSystemRef.current) return; physicsSystemRef.current.addBody({ id: body.id, position: { ...body.position }, velocity: body.velocity || { x: 0, y: 0 }, acceleration: { x: 0, y: 0 }, mass: body.mass || finalConfig.mass, radius: body.radius || 10, friction: 0.1, restitution: 0.8, fixed: body.fixed || false, forces: [] }); }, [finalConfig.mass]); // Remove body from physics system const removeBody = useCallback(id => { if (!physicsSystemRef.current) return; physicsSystemRef.current.removeBody(id); }, []); // Get body information const getBody = useCallback(id => { if (!physicsSystemRef.current) return; return physicsSystemRef.current.getBody(id); }, []); // Update physics configuration const updateConfig = useCallback(newConfig => { Object.assign(finalConfig, newConfig); if (physicsSystemRef.current) { physicsSystemRef.current.setGravity(finalConfig.gravity ? { x: 0, y: finalConfig.gravityStrength } : { x: 0, y: 0 }); physicsSystemRef.current.setBounds(finalConfig.bounds); } }, [finalConfig]); // Batch operations const batchUpdate = useCallback(updates => { updates.forEach(update => { switch (update.type) { case 'springTo': if (update.data) springTo(update.id, update.data); break; case 'impulse': if (update.data) impulse(update.id, update.data); break; case 'setPosition': if (update.data) setPosition(update.id, update.data); break; case 'addBody': if (update.data) addBody(update.data); break; case 'removeBody': removeBody(update.id); break; } }); }, [springTo, impulse, setPosition, addBody, removeBody]); return { positions, isAnimating, springTo, impulse, setPosition, addBody, removeBody, getBody, updateConfig, batchUpdate, physicsSystem: physicsSystemRef.current }; } // Hook for chain reactions (bodies affecting each other) function useChainReaction(bodies, connections, config = {}) { const galileoSprings = useGalileoSprings(bodies, config); const triggerChainReaction = useCallback((startId, initialImpulse) => { // Apply initial impulse galileoSprings.impulse(startId, initialImpulse); // Propagate through connections const visited = new Set(); const queue = [startId]; const propagate = () => { if (queue.length === 0) return; const currentId = queue.shift(); if (visited.has(currentId)) return; visited.add(currentId); // Find connections from current body const currentConnections = connections.filter(conn => conn.from === currentId); currentConnections.forEach(connection => { if (!visited.has(connection.to)) { // Calculate propagated impulse const currentBody = galileoSprings.getBody(currentId); if (currentBody) { const propagatedImpulse = { x: currentBody.velocity.x * connection.strength, y: currentBody.velocity.y * connection.strength }; galileoSprings.impulse(connection.to, propagatedImpulse); queue.push(connection.to); } } }); // Continue propagation setTimeout(propagate, 50); // Small delay for visual effect }; propagate(); }, [galileoSprings, connections]); return { ...galileoSprings, triggerChainReaction }; } // Hook for orbital mechanics function useOrbitalMechanics(centralBody, orbitingBodies, config = {}) { const { gravitationalConstant = 100, timeScale = 1, ...springConfig } = config; const allBodies = [centralBody, ...orbitingBodies]; const galileoSprings = useGalileoSprings(allBodies, springConfig); const updateOrbits = useCallback(() => { orbitingBodies.forEach(body => { const orbitingBody = galileoSprings.getBody(body.id); const central = galileoSprings.getBody(centralBody.id); if (!orbitingBody || !central) return; // Calculate gravitational force const dx = central.position.x - orbitingBody.position.x; const dy = central.position.y - orbitingBody.position.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { const force = gravitationalConstant * centralBody.mass * body.mass / (distance * distance); const forceX = force * dx / distance; const forceY = force * dy / distance; // Apply gravitational force galileoSprings.physicsSystem?.applyForce(body.id, { x: forceX * timeScale, y: forceY * timeScale }, 1 / 60); } }); }, [galileoSprings, centralBody, orbitingBodies, gravitationalConstant, timeScale]); useEffect(() => { const interval = setInterval(updateOrbits, 1000 / 60); // 60 FPS return () => clearInterval(interval); }, [updateOrbits]); return { ...galileoSprings, centralBody: centralBody.id, orbitingBodies: orbitingBodies.map(b => b.id), updateOrbits }; } // Hook for particle systems function useParticleSystem(emitterPosition, config = {}) { const { particleCount = 50, emissionRate = 10, particleLife = 3000, initialVelocity = { x: 0, y: -100 }, spread = 30, gravity = 9.81 } = config; const particles = []; const particleIds = []; // Create particle bodies for (let i = 0; i < particleCount; i++) { const angle = (Math.random() - 0.5) * spread * Math.PI / 180; const speed = Math.random() * 50 + 25; particles.push({ id: `particle-${i}`, position: { ...emitterPosition }, velocity: { x: initialVelocity.x + Math.cos(angle) * speed, y: initialVelocity.y + Math.sin(angle) * speed }, mass: 0.1, radius: 2 }); particleIds.push(`particle-${i}`); } const galileoSprings = useGalileoSprings(particles, { gravity: true, gravityStrength: gravity, enableCollisions: false }); const emit = useCallback(() => { // Reset all particles to emitter position particleIds.forEach((id, index) => { const angle = (Math.random() - 0.5) * spread * Math.PI / 180; const speed = Math.random() * 50 + 25; galileoSprings.setPosition(id, { ...emitterPosition }); galileoSprings.impulse(id, { x: initialVelocity.x + Math.cos(angle) * speed, y: initialVelocity.y + Math.sin(angle) * speed }); }); }, [galileoSprings, emitterPosition, spread, initialVelocity, particleIds]); const updateEmitter = useCallback(newPosition => { // This would update the emitter position for continuous emission // For now, just update the reference Object.assign(emitterPosition, newPosition); }, [emitterPosition]); return { ...galileoSprings, particleIds, emit, updateEmitter }; } // Hook for soft body physics function useSoftBodyPhysics(bodyDefinition, config = {}) { const nodes = bodyDefinition.nodes.map(node => ({ id: node.id, position: { x: node.x, y: node.y }, mass: node.mass, radius: 5 })); const galileoSprings = useGalileoSprings(nodes, config); const updateSprings = useCallback(() => { bodyDefinition.springs.forEach(spring => { const fromBody = galileoSprings.getBody(spring.from); const toBody = galileoSprings.getBody(spring.to); if (!fromBody || !toBody) return; const dx = toBody.position.x - fromBody.position.x; const dy = toBody.position.y - fromBody.position.y; const distance = Math.sqrt(dx * dx + dy * dy); const displacement = distance - spring.restLength; if (Math.abs(displacement) > 1) { const force = displacement * spring.stiffness; const forceX = force * dx / distance; const forceY = force * dy / distance; galileoSprings.impulse(spring.from, { x: forceX, y: forceY }); galileoSprings.impulse(spring.to, { x: -forceX, y: -forceY }); } }); }, [galileoSprings, bodyDefinition.springs]); useEffect(() => { const interval = setInterval(updateSprings, 1000 / 60); return () => clearInterval(interval); }, [updateSprings]); return { ...galileoSprings, springs: bodyDefinition.springs, updateSprings }; } export { useChainReaction, useGalileoSprings, useOrbitalMechanics, useParticleSystem, useSoftBodyPhysics }; //# sourceMappingURL=useGalileoSprings.js.map