// ============================================ // HERO — 3D parallax dashboard + QR scan + isometric scene + magnetic cursor // ============================================ const { useState, useEffect, useRef } = React; function MagneticCursor() { const dotRef = useRef(null); const ringRef = useRef(null); const particlesRef = useRef([]); useEffect(() => { if (window.innerWidth < 900) return; let mx = window.innerWidth / 2, my = window.innerHeight / 2; let rx = mx, ry = my; let dx = mx, dy = my; const move = (e) => { mx = e.clientX; my = e.clientY; if (dotRef.current) { dotRef.current.style.transform = `translate(${mx - 3}px, ${my - 3}px)`; } // spawn particle occasionally if (Math.random() > 0.85) { spawnParticle(mx, my); } }; const tick = () => { rx += (mx - rx) * 0.15; ry += (my - ry) * 0.15; if (ringRef.current) { ringRef.current.style.transform = `translate(${rx - 16}px, ${ry - 16}px)`; } requestAnimationFrame(tick); }; tick(); const onEnter = () => ringRef.current?.classList.add('hover'); const onLeave = () => ringRef.current?.classList.remove('hover'); window.addEventListener('mousemove', move); document.querySelectorAll('a, button, .hover-target').forEach(el => { el.addEventListener('mouseenter', onEnter); el.addEventListener('mouseleave', onLeave); }); return () => window.removeEventListener('mousemove', move); }, []); const spawnParticle = (x, y) => { const p = document.createElement('div'); p.className = 'cursor-particle'; p.style.cssText = `position:fixed;left:${x}px;top:${y}px;width:3px;height:3px;background:var(--cyan);border-radius:50%;pointer-events:none;z-index:9998;box-shadow:0 0 6px var(--cyan);transition:transform 0.8s ease-out,opacity 0.8s;`; document.body.appendChild(p); requestAnimationFrame(() => { const dx = (Math.random() - 0.5) * 40; const dy = (Math.random() - 0.5) * 40; p.style.transform = `translate(${dx}px, ${dy}px) scale(0)`; p.style.opacity = '0'; }); setTimeout(() => p.remove(), 800); }; return ( <>
> ); } function Hero() { const [tilt, setTilt] = useState({ x: 0, y: 0 }); const [scanPos, setScanPos] = useState(0); const [qrRevealed, setQrRevealed] = useState(false); const [screenIdx, setScreenIdx] = useState(0); const [kpi, setKpi] = useState({ total: 248, value: 4.2, maint: 12, alerts: 5 }); const heroRef = useRef(null); // Mouse parallax for dashboard useEffect(() => { const el = heroRef.current; if (!el) return; const move = (e) => { const r = el.getBoundingClientRect(); const px = (e.clientX - r.left) / r.width - 0.5; const py = (e.clientY - r.top) / r.height - 0.5; setTilt({ x: px * 10, y: -py * 10 }); }; el.addEventListener('mousemove', move); return () => el.removeEventListener('mousemove', move); }, []); // QR scan loop useEffect(() => { let t = 0; const id = setInterval(() => { t += 2; setScanPos(t % 110); if (t % 110 > 95 && !qrRevealed) setQrRevealed(true); if (t % 110 < 5) setQrRevealed(false); }, 40); return () => clearInterval(id); }, [qrRevealed]); // Screen cycling useEffect(() => { const id = setInterval(() => setScreenIdx(i => (i + 1) % 3), 3200); return () => clearInterval(id); }, []); // Live KPI ticker useEffect(() => { const id = setInterval(() => { setKpi(k => ({ total: k.total + (Math.random() > 0.55 ? 1 : 0), value: +(k.value + (Math.random() - 0.45) * 0.05).toFixed(2), maint: Math.max(4, Math.min(18, k.maint + (Math.random() > 0.5 ? 1 : -1))), alerts: Math.max(0, Math.min(9, k.alerts + (Math.random() > 0.65 ? 1 : -1))), })); }, 1800); return () => clearInterval(id); }, []); return (Controla inventarios, deprecia automáticamente y gestiona mantenimientos y consumibles desde una sola plataforma. Hecho para empresas que necesitan orden, trazabilidad y reportes en segundos.
{/* Trust row */}