feat: add target model

This commit is contained in:
xfy
2025-05-21 15:19:26 +08:00
parent 3ac7d42cc1
commit fd5501e1d0
7 changed files with 468 additions and 61 deletions

View File

@ -1,60 +1,24 @@
'use client';
import { Canvas } from '@react-three/fiber';
import { lazy, Suspense } from 'react';
import { lazy, Suspense, useMemo } from 'react';
import Loading from './loading';
import { PerspectiveCamera } from '@react-three/drei/core/PerspectiveCamera';
import { Leva, useControls } from 'leva';
import { useMediaQuery } from 'react-responsive';
const ComputerModel = lazy(
() => import('components/models/home/computer-model'),
);
const Target = lazy(() => import('components/models/home/target'));
const ComputerDesk = () => {
const c = useControls('RUARoom', {
positionX: {
value: 0,
min: -10,
max: 10,
step: 0.1,
},
positionY: {
value: 0,
min: -10,
max: 10,
step: 0.1,
},
positionZ: {
value: 0,
min: -10,
max: 10,
step: 0.01,
},
rotationX: {
value: 0,
min: -Math.PI,
max: Math.PI,
step: 0.01,
},
rotationY: {
value: 0,
min: -Math.PI,
max: Math.PI,
step: 0.01,
},
rotationZ: {
value: 0,
min: -Math.PI,
max: Math.PI,
step: 0.001,
},
scale: {
value: 1,
min: 0.1,
max: 10,
step: 0.01,
},
});
const isMobile = useMediaQuery({ query: '(max-width: 768px)' });
const scale = useMemo(() => {
if (isMobile) {
return 0.08;
}
return 0.1;
}, [isMobile]);
return (
<>
@ -63,14 +27,15 @@ const ComputerDesk = () => {
<ambientLight intensity={1} />
<directionalLight position={[10, 10, 10]} intensity={0.5} />
<ComputerModel
scale={0.1}
position={[0.6, -6.0, 0]}
scale={scale}
position={[0.6, -7.2, 0]}
rotation={[0, -Math.PI, 0]}
/>
<Target />
<PerspectiveCamera makeDefault position={[0, 0, 30]} />
</Suspense>
</Canvas>
<Leva />
{/* <Leva /> */}
</>
);
};

View File

@ -0,0 +1,89 @@
/**
* Samll target item in home page
*/
import * as THREE from 'three';
import React, { JSX, useMemo, useRef } from 'react';
import { useGLTF } from '@react-three/drei';
import { DRACOLoader, GLTF, GLTFLoader } from 'three-stdlib';
import { useLoader } from '@react-three/fiber';
import gsap from 'gsap';
import { useGSAP } from '@gsap/react';
import { useMediaQuery } from 'react-responsive';
type GLTFResult = GLTF & {
nodes: {
Cylinder016: THREE.Mesh;
Cylinder016_1: THREE.Mesh;
Cylinder016_2: THREE.Mesh;
};
materials: {
['Red.025']: THREE.MeshStandardMaterial;
['White.025']: THREE.MeshStandardMaterial;
['BrownDark.018']: THREE.MeshStandardMaterial;
};
};
export function Target(props: JSX.IntrinsicElements['group']) {
const { nodes, materials } = useLoader(
GLTFLoader,
'/models/target-processed.gltf',
(loader) => {
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('/libs/draco/');
loader.setDRACOLoader(dracoLoader);
},
) as unknown as GLTFResult;
// animation
const targetRef = useRef<THREE.Group>(null);
useGSAP(() => {
if (!targetRef.current) return;
gsap.to(targetRef.current.position, {
y: targetRef.current.position.y + 0.5,
duration: 1.5,
repeat: -1,
yoyo: true,
});
});
const isMobile = useMediaQuery({ query: '(max-width: 768px)' });
const position = useMemo<[number, number, number]>(() => {
if (isMobile) {
return [-5, -5, 3] as const;
}
return [-14, -8, 0] as const;
}, [isMobile]);
return (
<group {...props} dispose={null}>
<group
rotation={[Math.PI / 2, 0, -Math.PI / 8]}
position={position}
ref={targetRef}
>
<mesh
castShadow
receiveShadow
geometry={nodes.Cylinder016.geometry}
material={materials['Red.025']}
/>
<mesh
castShadow
receiveShadow
geometry={nodes.Cylinder016_1.geometry}
material={materials['White.025']}
/>
<mesh
castShadow
receiveShadow
geometry={nodes.Cylinder016_2.geometry}
material={materials['BrownDark.018']}
/>
</group>
</group>
);
}
useGLTF.preload('/models/target-processed.gltf');
export default Target;