60 lines
2 KiB
TypeScript
60 lines
2 KiB
TypeScript
import { useRef } from 'react';
|
|
import { useFrame } from '@react-three/fiber';
|
|
import * as THREE from 'three';
|
|
|
|
interface BeaconProps {
|
|
position: [number, number, number];
|
|
color?: string;
|
|
height?: number;
|
|
}
|
|
|
|
export function Beacon({ position, color = '#22d3ee', height = 15 }: BeaconProps) {
|
|
const meshRef = useRef<THREE.Mesh>(null);
|
|
const ringRef = useRef<THREE.Mesh>(null);
|
|
|
|
// Animate the beacon
|
|
useFrame((state) => {
|
|
if (meshRef.current) {
|
|
// Pulse opacity
|
|
const pulse = Math.sin(state.clock.elapsedTime * 3) * 0.3 + 0.5;
|
|
(meshRef.current.material as THREE.MeshBasicMaterial).opacity = pulse;
|
|
}
|
|
if (ringRef.current) {
|
|
// Expand and fade ring
|
|
const t = (state.clock.elapsedTime % 2) / 2;
|
|
ringRef.current.scale.set(1 + t * 3, 1, 1 + t * 3);
|
|
(ringRef.current.material as THREE.MeshBasicMaterial).opacity = 0.6 * (1 - t);
|
|
}
|
|
});
|
|
|
|
return (
|
|
<group position={position}>
|
|
{/* Vertical Light Beam */}
|
|
<mesh ref={meshRef} position={[0, height / 2, 0]}>
|
|
<cylinderGeometry args={[0.15, 0.4, height, 16, 1, true]} />
|
|
<meshBasicMaterial
|
|
color={color}
|
|
transparent
|
|
opacity={0.5}
|
|
side={THREE.DoubleSide}
|
|
depthWrite={false}
|
|
/>
|
|
</mesh>
|
|
|
|
{/* Ground Ring */}
|
|
<mesh ref={ringRef} rotation={[-Math.PI / 2, 0, 0]} position={[0, 0.05, 0]}>
|
|
<ringGeometry args={[0.3, 0.5, 32]} />
|
|
<meshBasicMaterial
|
|
color={color}
|
|
transparent
|
|
opacity={0.6}
|
|
side={THREE.DoubleSide}
|
|
depthWrite={false}
|
|
/>
|
|
</mesh>
|
|
|
|
{/* Core Glow */}
|
|
<pointLight color={color} intensity={2} distance={5} />
|
|
</group>
|
|
);
|
|
}
|