perfect-postcode/frontend/src/components/home/HexCanvas.tsx
2026-05-09 09:26:40 +01:00

87 lines
2.2 KiB
TypeScript

import { useMemo } from 'react';
const HEX_COUNT = 50;
interface HexConfig {
size: number;
opacity: number;
top: number;
left: number;
driftDuration: number;
bobDuration: number;
bobAmount: number;
delay: number;
reverse: boolean;
}
function generateHexes(): HexConfig[] {
const hexes: HexConfig[] = [];
for (let i = 0; i < HEX_COUNT; i++) {
const driftDuration = 40 + Math.random() * 60;
hexes.push({
size: 10 + Math.random() * 32,
opacity: 0.06 + Math.random() * 0.18,
top: Math.random() * 100,
left: Math.random() * 100,
driftDuration,
bobDuration: 6 + Math.random() * 8,
bobAmount: 8 + Math.random() * 30,
delay: -Math.random() * driftDuration,
reverse: Math.random() < 0.3,
});
}
return hexes;
}
export default function HexCanvas({
isDark = false,
animated = true,
className = '',
}: {
isDark?: boolean;
animated?: boolean;
className?: string;
}) {
const hexes = useMemo(generateHexes, []);
return (
<div
className={`absolute inset-0 overflow-hidden pointer-events-none ${className}`.trim()}
style={{ zIndex: 0 }}
>
{hexes.map((hex, i) => (
<div
key={i}
className="absolute"
style={{
top: `${hex.top}%`,
...(animated
? {
animation: `hex-drift ${hex.driftDuration}s linear ${hex.delay}s infinite${hex.reverse ? ' reverse' : ''}`,
}
: {
left: `${hex.left}%`,
transform: `translate(-50%, -50%) rotate(${hex.delay * 12}deg)`,
}),
}}
>
<div
className="bg-teal-500"
style={
{
width: hex.size,
height: (hex.size * 2) / Math.sqrt(3),
opacity: hex.opacity * (isDark ? 0.45 : 0.6),
clipPath: 'polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%)',
animation: animated
? `hex-bob ${hex.bobDuration}s ease-in-out infinite`
: undefined,
'--bob': `${hex.bobAmount}px`,
} as React.CSSProperties
}
/>
</div>
))}
</div>
);
}