176 lines
5.6 KiB
TypeScript
176 lines
5.6 KiB
TypeScript
'use client';
|
|
|
|
import React, { useRef, useMemo } from 'react';
|
|
import { Canvas, useFrame } from '@react-three/fiber';
|
|
import { Float, Stars } from '@react-three/drei';
|
|
import * as THREE from 'three';
|
|
import { useTranslations } from 'next-intl';
|
|
|
|
// ------------------- Three.js Components -------------------
|
|
|
|
const GeometryArt = () => {
|
|
const meshRef = useRef<THREE.Mesh>(null);
|
|
|
|
useFrame((state) => {
|
|
if (meshRef.current) {
|
|
meshRef.current.rotation.x = state.clock.getElapsedTime() * 0.1;
|
|
meshRef.current.rotation.y = state.clock.getElapsedTime() * 0.15;
|
|
}
|
|
});
|
|
|
|
return (
|
|
<Float speed={1.5} rotationIntensity={0.5} floatIntensity={0.5}>
|
|
<mesh ref={meshRef} position={[2, 0, 0]} scale={1.8}>
|
|
<icosahedronGeometry args={[1, 1]} />
|
|
<meshStandardMaterial
|
|
color="#C5A059"
|
|
wireframe
|
|
transparent
|
|
opacity={0.3}
|
|
roughness={0}
|
|
metalness={1}
|
|
/>
|
|
</mesh>
|
|
<mesh position={[2, 0, 0]} scale={0.5}>
|
|
<sphereGeometry args={[1, 32, 32]} />
|
|
<meshStandardMaterial
|
|
color="#C5A059"
|
|
emissive="#C5A059"
|
|
emissiveIntensity={0.5}
|
|
roughness={0.2}
|
|
metalness={1}
|
|
/>
|
|
</mesh>
|
|
</Float>
|
|
);
|
|
};
|
|
|
|
const ConnectingLines = () => {
|
|
// Create random connections mimicking a network
|
|
const count = 40;
|
|
const lines = useMemo(() => {
|
|
const points: [number, number, number][] = [];
|
|
// Use a fixed seed for consistent but pseudo-random positioning
|
|
const seed = 12345;
|
|
const seededRandom = (i: number) => {
|
|
const x = Math.sin(seed + i * 12.9898) * 43758.5453123;
|
|
return x - Math.floor(x);
|
|
};
|
|
for (let i = 0; i < count; i++) {
|
|
points.push([
|
|
(seededRandom(i * 3) - 0.5) * 10,
|
|
(seededRandom(i * 3 + 1) - 0.5) * 6,
|
|
(seededRandom(i * 3 + 2) - 0.5) * 5
|
|
]);
|
|
}
|
|
return points;
|
|
}, []);
|
|
|
|
const ref = useRef<THREE.Group>(null);
|
|
|
|
useFrame((state) => {
|
|
if (ref.current) {
|
|
ref.current.rotation.y = -state.clock.getElapsedTime() * 0.05;
|
|
}
|
|
});
|
|
|
|
return (
|
|
<group ref={ref}>
|
|
{lines.map((pos, i) => (
|
|
<mesh key={i} position={new THREE.Vector3(...pos)}>
|
|
<sphereGeometry args={[0.02, 8, 8]} />
|
|
<meshBasicMaterial color="#ffffff" opacity={0.4} transparent />
|
|
</mesh>
|
|
))}
|
|
</group>
|
|
)
|
|
}
|
|
|
|
const Scene = () => {
|
|
return (
|
|
<>
|
|
<ambientLight intensity={0.2} />
|
|
<pointLight position={[10, 10, 10]} intensity={1.5} color="#C5A059" />
|
|
<pointLight position={[-10, -10, -10]} intensity={0.5} color="#4c4c4c" />
|
|
<spotLight position={[0, 5, 0]} angle={0.3} penumbra={1} intensity={2} color="white" />
|
|
|
|
<GeometryArt />
|
|
<ConnectingLines />
|
|
<Stars radius={100} depth={50} count={5000} factor={4} saturation={0} fade speed={1} />
|
|
</>
|
|
);
|
|
};
|
|
|
|
// ------------------- Main Component -------------------
|
|
|
|
const Hero: React.FC = () => {
|
|
const t = useTranslations('Hero');
|
|
const tNav = useTranslations('Navbar');
|
|
|
|
return (
|
|
<section id="hero" className="relative w-full h-screen bg-feie-dark overflow-hidden">
|
|
{/* 3D Canvas Layer */}
|
|
<div className="absolute inset-0 z-0">
|
|
<Canvas camera={{ position: [0, 0, 6], fov: 45 }}>
|
|
<Scene />
|
|
</Canvas>
|
|
</div>
|
|
|
|
{/* Content Overlay */}
|
|
<div className="absolute inset-0 z-10 flex items-center">
|
|
<div className="container mx-auto px-6 grid grid-cols-1 md:grid-cols-2">
|
|
<div className="text-left space-y-6">
|
|
<div className="inline-block px-3 py-1 border border-feie-gold/50 rounded-full">
|
|
<span className="text-feie-gold text-xs font-serif tracking-widest uppercase">
|
|
Est. Nanjing • Technology Leader
|
|
</span>
|
|
</div>
|
|
<h1 className="text-5xl md:text-7xl font-serif text-feie-white leading-tight">
|
|
{t('title')} <br />
|
|
<span className="text-feie-gold italic">{t('subtitle')}</span>
|
|
</h1>
|
|
<p className="text-feie-white/70 text-lg md:text-xl font-light max-w-md leading-relaxed">
|
|
{t('description')}
|
|
</p>
|
|
<div className="pt-8 flex gap-4">
|
|
<a
|
|
href="#services"
|
|
className="px-8 py-3 bg-feie-gold text-feie-white font-serif hover:bg-yellow-600 transition-colors duration-300 rounded-sm"
|
|
>
|
|
{t('cta')}
|
|
</a>
|
|
<a
|
|
href="#contact"
|
|
className="px-8 py-3 border border-feie-white text-feie-white font-serif hover:bg-feie-white hover:text-feie-dark transition-colors duration-300 rounded-sm"
|
|
>
|
|
{tNav('contact')}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
{/* Right side is empty to allow the 3D visual to shine */}
|
|
<div className="hidden md:block"></div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Scroll indicator */}
|
|
<div className="absolute bottom-8 left-1/2 transform -translate-x-1/2 z-10 animate-bounce">
|
|
<svg
|
|
width="24"
|
|
height="24"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="#FFFFFF"
|
|
strokeWidth="2"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
className="opacity-50"
|
|
>
|
|
<path d="M12 5v14M19 12l-7 7-7-7"/>
|
|
</svg>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export default Hero;
|