| "use client"; |
|
|
| import { useRef, useMemo } from "react"; |
| import { Canvas, useFrame } from "@react-three/fiber"; |
| import { Float, MeshDistortMaterial, Environment } from "@react-three/drei"; |
| import * as THREE from "three"; |
|
|
| function FloatingWrench({ |
| position, |
| scale, |
| speed, |
| color, |
| }: { |
| position: [number, number, number]; |
| scale: number; |
| speed: number; |
| color: string; |
| }) { |
| const meshRef = useRef<THREE.Group>(null); |
|
|
| useFrame((state) => { |
| if (meshRef.current) { |
| meshRef.current.rotation.x = Math.sin(state.clock.elapsedTime * speed) * 0.3; |
| meshRef.current.rotation.y = state.clock.elapsedTime * speed * 0.5; |
| meshRef.current.rotation.z = Math.cos(state.clock.elapsedTime * speed) * 0.2; |
| } |
| }); |
|
|
| return ( |
| <Float speed={2} rotationIntensity={0.5} floatIntensity={1}> |
| <group ref={meshRef} position={position} scale={scale}> |
| {/* Wrench body */} |
| <mesh position={[0, 0, 0]}> |
| <boxGeometry args={[0.15, 1.2, 0.08]} /> |
| <MeshDistortMaterial |
| color={color} |
| distort={0.15} |
| speed={2} |
| metalness={0.8} |
| roughness={0.2} |
| /> |
| </mesh> |
| {/* Wrench head */} |
| <mesh position={[0, 0.65, 0]}> |
| <torusGeometry args={[0.2, 0.06, 8, 16, Math.PI * 1.2]} /> |
| <MeshDistortMaterial |
| color={color} |
| distort={0.1} |
| speed={1.5} |
| metalness={0.8} |
| roughness={0.2} |
| /> |
| </mesh> |
| {/* Wrench ring */} |
| <mesh position={[0, -0.6, 0]}> |
| <torusGeometry args={[0.15, 0.05, 8, 16]} /> |
| <MeshDistortMaterial |
| color={color} |
| distort={0.1} |
| speed={1.5} |
| metalness={0.8} |
| roughness={0.2} |
| /> |
| </mesh> |
| </group> |
| </Float> |
| ); |
| } |
|
|
| function FloatingGear({ |
| position, |
| scale, |
| speed, |
| color, |
| }: { |
| position: [number, number, number]; |
| scale: number; |
| speed: number; |
| color: string; |
| }) { |
| const meshRef = useRef<THREE.Mesh>(null); |
|
|
| useFrame((state) => { |
| if (meshRef.current) { |
| meshRef.current.rotation.x = state.clock.elapsedTime * speed * 0.3; |
| meshRef.current.rotation.z = state.clock.elapsedTime * speed * 0.5; |
| } |
| }); |
|
|
| return ( |
| <Float speed={1.5} rotationIntensity={0.3} floatIntensity={0.8}> |
| <mesh ref={meshRef} position={position} scale={scale}> |
| <torusGeometry args={[0.3, 0.1, 8, 12]} /> |
| <MeshDistortMaterial |
| color={color} |
| distort={0.2} |
| speed={speed} |
| metalness={0.7} |
| roughness={0.3} |
| /> |
| </mesh> |
| </Float> |
| ); |
| } |
|
|
| function FloatingCube({ |
| position, |
| scale, |
| speed, |
| color, |
| }: { |
| position: [number, number, number]; |
| scale: number; |
| speed: number; |
| color: string; |
| }) { |
| const meshRef = useRef<THREE.Mesh>(null); |
|
|
| useFrame((state) => { |
| if (meshRef.current) { |
| meshRef.current.rotation.x = state.clock.elapsedTime * speed * 0.4; |
| meshRef.current.rotation.y = state.clock.elapsedTime * speed * 0.6; |
| } |
| }); |
|
|
| return ( |
| <Float speed={2.5} rotationIntensity={0.8} floatIntensity={1.2}> |
| <mesh ref={meshRef} position={position} scale={scale}> |
| <octahedronGeometry args={[0.3, 0]} /> |
| <MeshDistortMaterial |
| color={color} |
| distort={0.3} |
| speed={speed} |
| metalness={0.6} |
| roughness={0.4} |
| wireframe |
| /> |
| </mesh> |
| </Float> |
| ); |
| } |
|
|
| function FloatingSphere({ |
| position, |
| scale, |
| speed, |
| color, |
| }: { |
| position: [number, number, number]; |
| scale: number; |
| speed: number; |
| color: string; |
| }) { |
| const meshRef = useRef<THREE.Mesh>(null); |
|
|
| useFrame((state) => { |
| if (meshRef.current) { |
| meshRef.current.rotation.y = state.clock.elapsedTime * speed * 0.3; |
| } |
| }); |
|
|
| return ( |
| <Float speed={1.8} rotationIntensity={0.2} floatIntensity={0.6}> |
| <mesh ref={meshRef} position={position} scale={scale}> |
| <sphereGeometry args={[0.25, 16, 16]} /> |
| <MeshDistortMaterial |
| color={color} |
| distort={0.4} |
| speed={speed * 1.5} |
| metalness={0.5} |
| roughness={0.5} |
| /> |
| </mesh> |
| </Float> |
| ); |
| } |
|
|
| function Scene() { |
| const objects = useMemo( |
| () => [ |
| { |
| type: "wrench", |
| position: [-3, 1, -2] as [number, number, number], |
| scale: 1.2, |
| speed: 0.3, |
| color: "#00F5FF", |
| }, |
| { |
| type: "gear", |
| position: [2, -1, -3] as [number, number, number], |
| scale: 0.8, |
| speed: 0.4, |
| color: "#9945FF", |
| }, |
| { |
| type: "cube", |
| position: [-1, 2, -4] as [number, number, number], |
| scale: 1, |
| speed: 0.5, |
| color: "#14F195", |
| }, |
| { |
| type: "wrench", |
| position: [3, 0, -2] as [number, number, number], |
| scale: 0.9, |
| speed: 0.35, |
| color: "#B87333", |
| }, |
| { |
| type: "sphere", |
| position: [0, -2, -3] as [number, number, number], |
| scale: 1.1, |
| speed: 0.25, |
| color: "#4A5568", |
| }, |
| { |
| type: "gear", |
| position: [-2, -1, -4] as [number, number, number], |
| scale: 0.6, |
| speed: 0.45, |
| color: "#00F5FF", |
| }, |
| { |
| type: "cube", |
| position: [1, 1, -5] as [number, number, number], |
| scale: 0.7, |
| speed: 0.55, |
| color: "#9945FF", |
| }, |
| { |
| type: "sphere", |
| position: [-3, -2, -3] as [number, number, number], |
| scale: 0.5, |
| speed: 0.3, |
| color: "#14F195", |
| }, |
| { |
| type: "wrench", |
| position: [2, 2, -4] as [number, number, number], |
| scale: 0.7, |
| speed: 0.4, |
| color: "#B87333", |
| }, |
| { |
| type: "gear", |
| position: [0, 0, -6] as [number, number, number], |
| scale: 1.5, |
| speed: 0.2, |
| color: "#00F5FF", |
| }, |
| ], |
| [] |
| ); |
|
|
| return ( |
| <> |
| <ambientLight intensity={0.3} /> |
| <pointLight position={[10, 10, 10]} intensity={1} color="#00F5FF" /> |
| <pointLight position={[-10, -10, -10]} intensity={0.5} color="#9945FF" /> |
| <pointLight position={[0, 5, 5]} intensity={0.8} color="#14F195" /> |
| |
| {objects.map((obj, index) => { |
| switch (obj.type) { |
| case "wrench": |
| return ( |
| <FloatingWrench |
| key={index} |
| position={obj.position} |
| scale={obj.scale} |
| speed={obj.speed} |
| color={obj.color} |
| /> |
| ); |
| case "gear": |
| return ( |
| <FloatingGear |
| key={index} |
| position={obj.position} |
| scale={obj.scale} |
| speed={obj.speed} |
| color={obj.color} |
| /> |
| ); |
| case "cube": |
| return ( |
| <FloatingCube |
| key={index} |
| position={obj.position} |
| scale={obj.scale} |
| speed={obj.speed} |
| color={obj.color} |
| /> |
| ); |
| case "sphere": |
| return ( |
| <FloatingSphere |
| key={index} |
| position={obj.position} |
| scale={obj.scale} |
| speed={obj.speed} |
| color={obj.color} |
| /> |
| ); |
| default: |
| return null; |
| } |
| })} |
| |
| {/* Particles */} |
| <Particles /> |
| </> |
| ); |
| } |
|
|
| function Particles() { |
| const pointsRef = useRef<THREE.Points>(null); |
| const count = 200; |
|
|
| const positions = useMemo(() => { |
| const pos = new Float32Array(count * 3); |
| for (let i = 0; i < count; i++) { |
| pos[i * 3] = (Math.random() - 0.5) * 20; |
| pos[i * 3 + 1] = (Math.random() - 0.5) * 20; |
| pos[i * 3 + 2] = (Math.random() - 0.5) * 20; |
| } |
| return pos; |
| }, []); |
|
|
| useFrame((state) => { |
| if (pointsRef.current) { |
| pointsRef.current.rotation.y = state.clock.elapsedTime * 0.02; |
| pointsRef.current.rotation.x = state.clock.elapsedTime * 0.01; |
| } |
| }); |
|
|
| return ( |
| <points ref={pointsRef}> |
| <bufferGeometry> |
| <bufferAttribute |
| attach="attributes-position" |
| args={[positions, 3]} |
| count={count} |
| array={positions} |
| itemSize={3} |
| /> |
| </bufferGeometry> |
| <pointsMaterial |
| size={0.03} |
| color="#00F5FF" |
| transparent |
| opacity={0.6} |
| sizeAttenuation |
| /> |
| </points> |
| ); |
| } |
|
|
| export function FloatingTools() { |
| return ( |
| <Canvas |
| camera={{ position: [0, 0, 8], fov: 60 }} |
| style={{ background: "transparent" }} |
| dpr={[1, 2]} |
| > |
| <Scene /> |
| </Canvas> |
| ); |
| } |
|
|