mirror of
https://github.com/DefectingCat/DefectingCat.github.io
synced 2025-07-16 01:01:38 +00:00
feat: add target model
This commit is contained in:
@ -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 /> */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
89
components/models/home/target.tsx
Normal file
89
components/models/home/target.tsx
Normal 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;
|
Reference in New Issue
Block a user