Files
gov/components/TechVisualizer.tsx

167 lines
4.9 KiB
TypeScript
Executable File

import React, { useRef, useMemo, useEffect } from 'react';
import { Canvas, useFrame } from '@react-three/fiber';
import { OrbitControls, PerspectiveCamera } from '@react-three/drei';
import * as THREE from 'three';
// Add global declaration to fix TypeScript errors with React Three Fiber elements
declare module 'react' {
namespace JSX {
interface IntrinsicElements {
points: any;
bufferGeometry: any;
bufferAttribute: any;
pointsMaterial: any;
ambientLight: any;
}
}
}
declare global {
namespace JSX {
interface IntrinsicElements {
points: any;
bufferGeometry: any;
bufferAttribute: any;
pointsMaterial: any;
ambientLight: any;
}
}
}
interface TechVisualizerProps {
mode: 'server' | 'monitor' | 'hidden';
}
const ParticleMorph = ({ mode }: { mode: 'server' | 'monitor' | 'hidden' }) => {
const meshRef = useRef<THREE.Points>(null);
const COUNT = 3000;
// Define geometries
const data = useMemo(() => {
const pos = new Float32Array(COUNT * 3);
const col = new Float32Array(COUNT * 3);
const serverPos = new Float32Array(COUNT * 3);
const monitorPos = new Float32Array(COUNT * 3);
// SERVER SHAPE (Rectangular Tower)
for (let i = 0; i < COUNT; i++) {
const x = (Math.random() - 0.5) * 2; // Thin width
const y = (Math.random() - 0.5) * 6; // Tall
const z = (Math.random() - 0.5) * 2; // Depth
serverPos[i * 3] = x;
serverPos[i * 3 + 1] = y;
serverPos[i * 3 + 2] = z;
// Add some "layers" to make it look like a rack
if (Math.random() > 0.8) {
serverPos[i * 3] *= 1.2;
serverPos[i * 3 + 2] *= 1.2;
}
}
// MONITOR SHAPE (Curved Plane)
for (let i = 0; i < COUNT; i++) {
const x = (Math.random() - 0.5) * 8; // Wide
const y = (Math.random() - 0.5) * 3.5; // Aspect ratio
// Curve the Z based on X (parabolic)
const z = Math.pow(x * 0.3, 2) - 2;
monitorPos[i * 3] = x;
monitorPos[i * 3 + 1] = y + 0.5; // Lift up slightly
monitorPos[i * 3 + 2] = z;
// Bezel/Frame particles
if (Math.random() > 0.95) {
monitorPos[i * 3 + 2] += 0.1;
}
}
// Initial Positions (start at server)
for (let i = 0; i < COUNT * 3; i++) {
pos[i] = serverPos[i];
col[i] = 1; // Initial white/cyan mix logic in shader or simple color
}
return { positions: pos, colors: col, serverPos, monitorPos };
}, []);
// Buffer attributes
const bufferRef = useRef<THREE.BufferAttribute>(null);
useFrame((state, delta) => {
if (!meshRef.current || !bufferRef.current) return;
const target = mode === 'monitor' ? data.monitorPos : data.serverPos;
const current = bufferRef.current.array as Float32Array;
// Morph speed
const speed = 4 * delta;
// Visibility transition
const isHidden = mode === 'hidden';
for (let i = 0; i < COUNT; i++) {
const ix = i * 3;
const iy = i * 3 + 1;
const iz = i * 3 + 2;
if (isHidden) {
// Explode/Hide
current[ix] = THREE.MathUtils.lerp(current[ix], current[ix] * 1.01, speed);
current[iy] = THREE.MathUtils.lerp(current[iy], current[iy] + 10, speed);
} else {
// Standard Morph
current[ix] = THREE.MathUtils.lerp(current[ix], target[ix], speed);
current[iy] = THREE.MathUtils.lerp(current[iy], target[iy], speed);
current[iz] = THREE.MathUtils.lerp(current[iz], target[iz], speed);
// Add subtle noise/floating
current[iy] += Math.sin(state.clock.elapsedTime + current[ix]) * 0.002;
}
}
bufferRef.current.needsUpdate = true;
// Rotate entire mesh slowly
meshRef.current.rotation.y += delta * 0.1;
});
return (
<points ref={meshRef}>
<bufferGeometry>
<bufferAttribute
ref={bufferRef}
attach="attributes-position"
array={data.positions}
count={data.positions.length / 3}
itemSize={3}
/>
</bufferGeometry>
<pointsMaterial
size={0.04}
color={mode === 'monitor' ? "#ff4b4b" : "#20e3b2"} // Red for monitor (Anime.js accent), Cyan for Server
transparent
opacity={0.8}
sizeAttenuation
blending={THREE.AdditiveBlending}
/>
</points>
);
};
const TechVisualizer: React.FC<TechVisualizerProps> = ({ mode }) => {
return (
<div className={`fixed inset-0 z-[1] transition-opacity duration-700 pointer-events-none ${mode === 'hidden' ? 'opacity-0' : 'opacity-60'}`}>
<Canvas gl={{ antialias: true, alpha: true }}>
<PerspectiveCamera makeDefault position={[0, 0, 8]} fov={50} />
<ambientLight intensity={0.5} />
<ParticleMorph mode={mode} />
</Canvas>
</div>
);
};
export default TechVisualizer;