Gemma-4-WebGPU / src /components /OrbitalHero.jsx
shreyask's picture
Upload folder using huggingface_hub
c38a89e verified
import { useEffect, useRef } from "react";
// Identical ellipses, evenly spaced tilts (45° apart)
const ORBITALS = [
{ icon: "📷", label: "Image", tilt: 0, rx: 130, ry: 45, duration: 10, phase: 0 },
{ icon: "🎤", label: "Audio", tilt: 45, rx: 130, ry: 45, duration: 13, phase: 0 },
{ icon: "💬", label: "Text", tilt: 90, rx: 130, ry: 45, duration: 11, phase: 0 },
{ icon: "🎬", label: "Video", tilt: 135, rx: 130, ry: 45, duration: 15, phase: 0 },
];
// Precompute tilt radians
const ORBITAL_DATA = ORBITALS.map((o) => ({
...o,
tiltRad: (o.tilt * Math.PI) / 180,
}));
export default function OrbitalHero() {
const itemRefs = useRef([]);
useEffect(() => {
let frame;
const start = performance.now();
function tick() {
const time = (performance.now() - start) / 1000;
for (let i = 0; i < ORBITAL_DATA.length; i++) {
const o = ORBITAL_DATA[i];
const el = itemRefs.current[i];
if (!el) continue;
const angle = ((time / o.duration) * Math.PI * 2) + o.phase;
const ex = o.rx * Math.cos(angle);
const ey = o.ry * Math.sin(angle);
const x = ex * Math.cos(o.tiltRad) - ey * Math.sin(o.tiltRad);
const y = ex * Math.sin(o.tiltRad) + ey * Math.cos(o.tiltRad);
const z = Math.sin(angle);
el.style.left = `calc(50% + ${x}px)`;
el.style.top = `calc(50% + ${y}px)`;
el.style.zIndex = z > 0 ? 20 : 1;
el.style.opacity = 0.5 + z * 0.5;
}
frame = requestAnimationFrame(tick);
}
frame = requestAnimationFrame(tick);
return () => cancelAnimationFrame(frame);
}, []);
return (
<div className="relative w-80 h-80 mx-auto">
<svg className="absolute inset-0 w-full h-full" viewBox="-160 -160 320 320">
{ORBITAL_DATA.map((o) => (
<ellipse
key={o.label}
cx="0"
cy="0"
rx={o.rx}
ry={o.ry}
fill="none"
stroke="var(--color-text-secondary)"
opacity="0.15"
strokeWidth="1"
transform={`rotate(${o.tilt})`}
/>
))}
</svg>
<div className="absolute inset-0 flex items-center justify-center z-10">
<div className="w-16 h-16 rounded-2xl bg-gradient-to-br from-[#3186FF] to-[#4FA0FF] flex items-center justify-center text-white text-2xl font-bold shadow-[0_0_40px_rgba(60,144,255,0.3)]">
G
</div>
</div>
<div className="absolute inset-0 flex items-center justify-center pointer-events-none">
<div className="w-28 h-28 rounded-full bg-[var(--color-blue)]/8 animate-pulse" />
</div>
{ORBITAL_DATA.map((o, i) => (
<div
key={o.label}
ref={(el) => (itemRefs.current[i] = el)}
className="absolute flex flex-col items-center gap-0.5 px-2.5 py-1.5 bg-[var(--color-surface)]/90 border border-[var(--color-outline)] rounded-xl backdrop-blur-sm"
style={{ transform: "translate(-50%, -50%)" }}
>
<span className="text-base leading-none">{o.icon}</span>
<span className="text-[9px] text-[var(--color-text-secondary)] font-medium leading-none">{o.label}</span>
</div>
))}
</div>
);
}