Skip to content

Commit 4c44ac8

Browse files
committed
feat: GameBoy 컴포넌트 및 화면 추가, 상태 관리 기능 구현
1 parent 11a4674 commit 4c44ac8

File tree

13 files changed

+160
-14
lines changed

13 files changed

+160
-14
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@
1111
"dependencies": {
1212
"@astrojs/react": "^4.3.0",
1313
"@lucide/astro": "^0.542.0",
14+
"@nanostores/react": "^1.0.0",
1415
"@react-three/drei": "^10.7.4",
1516
"@react-three/fiber": "^9.3.0",
1617
"@react-three/postprocessing": "^3.0.4",
1718
"@tailwindcss/vite": "^4.1.13",
1819
"astro": "^5.13.5",
20+
"lucide-react": "^0.544.0",
21+
"nanostores": "^1.0.1",
1922
"pretendard": "^1.3.9",
2023
"react": "^19.1.1",
2124
"react-dom": "^19.1.1",

pnpm-lock.yaml

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/images/blog.png

339 KB
Loading

public/images/blog.webp

25.7 KB
Loading

public/images/goc.png

50.4 KB
Loading

public/images/tape.png

64.5 KB
Loading

public/images/your-planet.webp

91.5 KB
Loading

src/assets/app.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ License: none (public domain)
6161

6262
body {
6363
line-height: 1;
64-
font-family: "Pretendard", sans-serif;
64+
font-family: "consolas", sans-serif;
6565
}
6666

6767
ol, ul {

src/components/GameBoy.tsx

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import * as THREE from 'three'
88
import React, {type JSX, useState} from 'react'
99
import {useCursor, useGLTF} from '@react-three/drei'
1010
import {type GLTF} from 'three-stdlib'
11+
import GameBoyScreen from "./GameBoyScreen.tsx";
12+
import {$currentIndex} from '../stores/gameboy.ts';
13+
import {works} from "../data/works.ts";
1114

1215
type GLTFResult = GLTF & {
1316
nodes: {
@@ -27,13 +30,28 @@ export default function Model(props: JSX.IntrinsicElements['group']) {
2730
const {nodes, materials} = useGLTF('/models/GameBoy-transformed.glb') as unknown as GLTFResult
2831
return (
2932
<group {...props} dispose={null}>
33+
<GameBoyScreen/>
3034
<mesh castShadow receiveShadow geometry={nodes.GameBoy.geometry} material={materials.lambert2SG} rotation={[Math.PI / 2, 0, 0]}/>
31-
<Button>
32-
<mesh castShadow receiveShadow geometry={nodes.A.geometry} material={materials.lambert2SG} rotation={[Math.PI / 2, 0, 0]}/>
33-
</Button>
34-
<Button>
35+
<Button onClick={() => {
36+
const index = $currentIndex.get();
37+
if (index === 0) {
38+
$currentIndex.set(works.length - 1);
39+
return;
40+
}
41+
$currentIndex.set(index - 1);
42+
}}>
3543
<mesh castShadow receiveShadow geometry={nodes.B.geometry} material={materials.lambert2SG} rotation={[Math.PI / 2, 0, 0]}/>
3644
</Button>
45+
<Button onClick={() => {
46+
const index = $currentIndex.get();
47+
if (index === works.length - 1) {
48+
$currentIndex.set(0);
49+
return;
50+
}
51+
$currentIndex.set(index + 1);
52+
}}>
53+
<mesh castShadow receiveShadow geometry={nodes.A.geometry} material={materials.lambert2SG} rotation={[Math.PI / 2, 0, 0]}/>
54+
</Button>
3755
<group>
3856
<mesh castShadow receiveShadow geometry={nodes['+'].geometry} material={materials.lambert2SG} rotation={[Math.PI / 2, 0, 0]}/>
3957
</group>
@@ -49,11 +67,19 @@ export default function Model(props: JSX.IntrinsicElements['group']) {
4967

5068
useGLTF.preload('/models/GameBoy-transformed.glb')
5169

52-
const Button = ({children}: { children: React.ReactNode }) => {
70+
const Button = (
71+
{
72+
onClick,
73+
children,
74+
}: {
75+
onClick?: () => void;
76+
children: React.ReactNode;
77+
}) => {
5378
const [hovered, setHovered] = useState(false);
5479
const [clicked, setClicked] = useState(false);
5580
useCursor(hovered)
5681
return <group
82+
onClick={onClick}
5783
onPointerOver={() => setHovered(true)}
5884
onPointerOut={() => {
5985
setHovered(false);

src/components/GameBoyScreen.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import {Html} from "@react-three/drei";
2+
import {DoubleSide} from "three";
3+
import {useStore} from "@nanostores/react";
4+
import {$currentIndex} from "../stores/gameboy.ts";
5+
import {works} from "../data/works.ts";
6+
import {SquareArrowOutUpRight, Gamepad2, Pointer} from "lucide-react"
7+
8+
export default function GameBoyScreen() {
9+
10+
return <Html occlude={"raycast"} transform castShadow receiveShadow scale={.85} position={[0, 8.05, .75]}
11+
material={<meshStandardMaterial side={DoubleSide}/>}>
12+
<Thumbnail/>
13+
</Html>
14+
}
15+
16+
export const Thumbnail = () => {
17+
18+
const currentIndex = useStore($currentIndex);
19+
const work = works[currentIndex];
20+
21+
return <div className={"w-50 h-50 bg-black bg-center bg-cover relative"}
22+
style={{
23+
backgroundImage: `url(${work.imageUrl})`,
24+
}}
25+
>
26+
<div className="absolute inset-0 flex flex-col justify-between items-center p-2 text-bright text-2xl text-shadow-lg opacity-100 hover:opacity-0 transition-all">
27+
<div className={"flex flex-row justify-center items-center gap-1"}>
28+
<Pointer size={24}/>
29+
Hover Me
30+
<Pointer size={24}/>
31+
</div>
32+
<div className="flex self-end gap-1 py-1 px-2 rounded text-sm bg-black/80">
33+
<Gamepad2 size={18}/> A/B Navigate
34+
</div>
35+
</div>
36+
<div className="absolute inset-0 flex flex-col justify-center items-center text-bright text-xl gap-1 cursor-pointer bg-black/80 transition-all opacity-0 hover:opacity-100"
37+
onClick={() => window.open(work.url)}
38+
>
39+
<div className={"flex flex-row items-center gap-1"}>{work.title} <SquareArrowOutUpRight size={18}/></div>
40+
<span className={"text-sm whitespace-pre-wrap text-center px-2"}>{work.description}</span>
41+
</div>
42+
</div>
43+
}

0 commit comments

Comments
 (0)