mirror of
https://github.com/DefectingCat/DefectingCat.github.io
synced 2025-07-15 08:41:37 +00:00
feat: add target model
This commit is contained in:
25
app/page.tsx
25
app/page.tsx
@ -18,23 +18,24 @@ export default function Page() {
|
||||
<ComputerDesk />
|
||||
</div>
|
||||
|
||||
<div className="text-6xl flex items-center">
|
||||
<span
|
||||
<div className="text-center pt-20">
|
||||
<h1
|
||||
className={clsx(
|
||||
'font-semibold font-Lobster tracking-wide',
|
||||
'pb-12 text-5xl sm:text-6xl',
|
||||
styles.gradient,
|
||||
)}
|
||||
>
|
||||
Hello World!
|
||||
</span>
|
||||
<span className="ml-3">
|
||||
<Image
|
||||
src="/images/img/hands.svg"
|
||||
alt="hands"
|
||||
width={36}
|
||||
height={36}
|
||||
/>
|
||||
</span>
|
||||
Hello World
|
||||
</h1>
|
||||
<h2
|
||||
className={clsx(
|
||||
'font-semibold font-Lobster tracking-wide',
|
||||
'text-4xl sm:text-5xl',
|
||||
)}
|
||||
>
|
||||
TECH OTAKUS SAVE THE WORLD
|
||||
</h2>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
|
@ -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;
|
@ -22,6 +22,7 @@
|
||||
"@docsearch/css": "^3.9.0",
|
||||
"@docsearch/react": "^3.9.0",
|
||||
"@giscus/react": "^3.1.0",
|
||||
"@gsap/react": "^2.1.2",
|
||||
"@octokit/core": "^6.1.5",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^14.0.0",
|
||||
"@react-spring/three": "^10.0.0",
|
||||
@ -30,12 +31,14 @@
|
||||
"@tailwindcss/postcss": "^4.1.7",
|
||||
"algoliasearch": "^5.25.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"gsap": "^3.13.0",
|
||||
"next": "15.3.2",
|
||||
"next-mdx-remote": "^5.0.0",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-responsive": "^10.0.1",
|
||||
"rehype-highlight": "^7.0.0",
|
||||
"rehype-react": "^8.0.0",
|
||||
"rehype-slug": "^6.0.0",
|
||||
|
61
pnpm-lock.yaml
generated
61
pnpm-lock.yaml
generated
@ -29,6 +29,9 @@ importers:
|
||||
'@giscus/react':
|
||||
specifier: ^3.1.0
|
||||
version: 3.1.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@gsap/react':
|
||||
specifier: ^2.1.2
|
||||
version: 2.1.2(gsap@3.13.0)(react@19.1.0)
|
||||
'@octokit/core':
|
||||
specifier: ^6.1.5
|
||||
version: 6.1.5
|
||||
@ -53,6 +56,9 @@ importers:
|
||||
dayjs:
|
||||
specifier: ^1.11.13
|
||||
version: 1.11.13
|
||||
gsap:
|
||||
specifier: ^3.13.0
|
||||
version: 3.13.0
|
||||
next:
|
||||
specifier: 15.3.2
|
||||
version: 15.3.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.0)
|
||||
@ -71,6 +77,9 @@ importers:
|
||||
react-icons:
|
||||
specifier: ^5.5.0
|
||||
version: 5.5.0(react@19.1.0)
|
||||
react-responsive:
|
||||
specifier: ^10.0.1
|
||||
version: 10.0.1(react@19.1.0)
|
||||
rehype-highlight:
|
||||
specifier: ^7.0.0
|
||||
version: 7.0.1
|
||||
@ -449,6 +458,12 @@ packages:
|
||||
react: ^16 || ^17 || ^18 || ^19
|
||||
react-dom: ^16 || ^17 || ^18 || ^19
|
||||
|
||||
'@gsap/react@2.1.2':
|
||||
resolution: {integrity: sha512-JqliybO1837UcgH2hVOM4VO+38APk3ECNrsuSM4MuXp+rbf+/2IG2K1YJiqfTcXQHH7XlA0m3ykniFYstfq0Iw==}
|
||||
peerDependencies:
|
||||
gsap: ^3.12.5
|
||||
react: '>=17'
|
||||
|
||||
'@humanwhocodes/config-array@0.13.0':
|
||||
resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==}
|
||||
engines: {node: '>=10.10.0'}
|
||||
@ -1608,6 +1623,9 @@ packages:
|
||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
css-mediaquery@0.1.2:
|
||||
resolution: {integrity: sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q==}
|
||||
|
||||
csstype@3.1.3:
|
||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
||||
|
||||
@ -2133,6 +2151,9 @@ packages:
|
||||
resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==}
|
||||
engines: {node: '>=6.0'}
|
||||
|
||||
gsap@3.13.0:
|
||||
resolution: {integrity: sha512-QL7MJ2WMjm1PHWsoFrAQH/J8wUeqZvMtHO58qdekHpCfhvhSL4gSiz6vJf5EeMP0LOn3ZCprL2ki/gjED8ghVw==}
|
||||
|
||||
gzip-size@6.0.0:
|
||||
resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==}
|
||||
engines: {node: '>=10'}
|
||||
@ -2198,6 +2219,9 @@ packages:
|
||||
html-escaper@2.0.2:
|
||||
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
|
||||
|
||||
hyphenate-style-name@1.1.0:
|
||||
resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==}
|
||||
|
||||
iconv-lite@0.6.3:
|
||||
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@ -2608,6 +2632,9 @@ packages:
|
||||
markdown-table@3.0.4:
|
||||
resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
|
||||
|
||||
matchmediaquery@0.4.2:
|
||||
resolution: {integrity: sha512-wrZpoT50ehYOudhDjt/YvUJc6eUzcdFPdmbizfgvswCKNHD1/OBOHYJpHie+HXpu6bSkEGieFMYk6VuutaiRfA==}
|
||||
|
||||
mdast-util-find-and-replace@3.0.1:
|
||||
resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==}
|
||||
|
||||
@ -3072,6 +3099,12 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^19.0.0
|
||||
|
||||
react-responsive@10.0.1:
|
||||
resolution: {integrity: sha512-OM5/cRvbtUWEX8le8RCT8scA8y2OPtb0Q/IViEyCEM5FBN8lRrkUOZnu87I88A6njxDldvxG+rLBxWiA7/UM9g==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
|
||||
react-use-measure@2.1.7:
|
||||
resolution: {integrity: sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==}
|
||||
peerDependencies:
|
||||
@ -3238,6 +3271,9 @@ packages:
|
||||
resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
shallow-equal@3.1.0:
|
||||
resolution: {integrity: sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg==}
|
||||
|
||||
sharp@0.34.1:
|
||||
resolution: {integrity: sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
@ -4116,6 +4152,11 @@ snapshots:
|
||||
react: 19.1.0
|
||||
react-dom: 19.1.0(react@19.1.0)
|
||||
|
||||
'@gsap/react@2.1.2(gsap@3.13.0)(react@19.1.0)':
|
||||
dependencies:
|
||||
gsap: 3.13.0
|
||||
react: 19.1.0
|
||||
|
||||
'@humanwhocodes/config-array@0.13.0':
|
||||
dependencies:
|
||||
'@humanwhocodes/object-schema': 2.0.3
|
||||
@ -5323,6 +5364,8 @@ snapshots:
|
||||
shebang-command: 2.0.0
|
||||
which: 2.0.2
|
||||
|
||||
css-mediaquery@0.1.2: {}
|
||||
|
||||
csstype@3.1.3: {}
|
||||
|
||||
d@1.0.2:
|
||||
@ -6021,6 +6064,8 @@ snapshots:
|
||||
section-matter: 1.0.0
|
||||
strip-bom-string: 1.0.0
|
||||
|
||||
gsap@3.13.0: {}
|
||||
|
||||
gzip-size@6.0.0:
|
||||
dependencies:
|
||||
duplexer: 0.1.2
|
||||
@ -6117,6 +6162,8 @@ snapshots:
|
||||
|
||||
html-escaper@2.0.2: {}
|
||||
|
||||
hyphenate-style-name@1.1.0: {}
|
||||
|
||||
iconv-lite@0.6.3:
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
@ -6492,6 +6539,10 @@ snapshots:
|
||||
|
||||
markdown-table@3.0.4: {}
|
||||
|
||||
matchmediaquery@0.4.2:
|
||||
dependencies:
|
||||
css-mediaquery: 0.1.2
|
||||
|
||||
mdast-util-find-and-replace@3.0.1:
|
||||
dependencies:
|
||||
'@types/mdast': 4.0.4
|
||||
@ -7237,6 +7288,14 @@ snapshots:
|
||||
react: 19.1.0
|
||||
scheduler: 0.25.0
|
||||
|
||||
react-responsive@10.0.1(react@19.1.0):
|
||||
dependencies:
|
||||
hyphenate-style-name: 1.1.0
|
||||
matchmediaquery: 0.4.2
|
||||
prop-types: 15.8.1
|
||||
react: 19.1.0
|
||||
shallow-equal: 3.1.0
|
||||
|
||||
react-use-measure@2.1.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
|
||||
dependencies:
|
||||
react: 19.1.0
|
||||
@ -7481,6 +7540,8 @@ snapshots:
|
||||
is-plain-object: 2.0.4
|
||||
split-string: 3.1.0
|
||||
|
||||
shallow-equal@3.1.0: {}
|
||||
|
||||
sharp@0.34.1:
|
||||
dependencies:
|
||||
color: 4.2.3
|
||||
|
Binary file not shown.
288
public/models/target-processed.gltf
Normal file
288
public/models/target-processed.gltf
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user