This commit is contained in:
quantulr
2024-01-02 09:22:09 +08:00
parent 784fe1d051
commit e54a837654
9 changed files with 1137 additions and 383 deletions

View File

@ -13,15 +13,16 @@
"dependencies": { "dependencies": {
"@chakra-ui/icons": "^2.1.1", "@chakra-ui/icons": "^2.1.1",
"@chakra-ui/react": "^2.8.2", "@chakra-ui/react": "^2.8.2",
"@emotion/react": "^11.11.1", "@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0", "@emotion/styled": "^11.11.0",
"@react-three/drei": "^9.92.1", "@react-three/drei": "^9.92.7",
"@react-three/fiber": "^8.15.12", "@react-three/fiber": "^8.15.12",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/uuid": "^9.0.7", "@types/uuid": "^9.0.7",
"ahooks": "^3.7.8", "ahooks": "^3.7.8",
"fabric": "^5.3.0", "fabric": "^5.3.0",
"framer-motion": "^10.16.16", "framer-motion": "^10.16.16",
"leva": "^0.9.35",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
@ -32,23 +33,23 @@
}, },
"devDependencies": { "devDependencies": {
"@types/fabric": "^5.3.6", "@types/fabric": "^5.3.6",
"@types/node": "^20.10.4", "@types/node": "^20.10.5",
"@types/react": "^18.2.45", "@types/react": "^18.2.45",
"@types/react-dom": "^18.2.17", "@types/react-dom": "^18.2.18",
"@types/three": "^0.159.0", "@types/three": "^0.159.0",
"@typescript-eslint/eslint-plugin": "^6.14.0", "@typescript-eslint/eslint-plugin": "^6.16.0",
"@typescript-eslint/parser": "^6.14.0", "@typescript-eslint/parser": "^6.16.0",
"@vitejs/plugin-react-swc": "^3.5.0", "@vitejs/plugin-react-swc": "^3.5.0",
"autoprefixer": "^10.4.16", "autoprefixer": "^10.4.16",
"eslint": "^8.55.0", "eslint": "^8.56.0",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5", "eslint-plugin-react-refresh": "^0.4.5",
"postcss": "^8.4.32", "postcss": "^8.4.32",
"prettier": "^3.1.1", "prettier": "^3.1.1",
"prettier-plugin-tailwindcss": "^0.5.9", "prettier-plugin-tailwindcss": "^0.5.9",
"sass": "^1.69.5", "sass": "^1.69.5",
"tailwindcss": "^3.3.6", "tailwindcss": "^3.4.0",
"typescript": "^5.3.3", "typescript": "^5.3.3",
"vite": "^5.0.8" "vite": "^5.0.10"
} }
} }

1375
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -65,7 +65,7 @@ const LogoPanel = () => {
{ {
id, id,
url: URL.createObjectURL(files[0]), url: URL.createObjectURL(files[0]),
postion: new Vector3(0, 0, 0), position: new Vector3(0, 0, 0),
type: StickerType.logo, type: StickerType.logo,
scale: activeModel ? 1.5 : 0.05, scale: activeModel ? 1.5 : 0.05,
}, },

View File

@ -4,18 +4,57 @@ import useModelStore from "@/store/useModelStore.ts";
import TextureSelectorPanel from "./TextureSelectorPanel"; import TextureSelectorPanel from "./TextureSelectorPanel";
import LogoPanel from "./LogoPanel"; import LogoPanel from "./LogoPanel";
import TextLabelPanel from "./TextLabelPanel"; import TextLabelPanel from "./TextLabelPanel";
import { useState } from "react";
const RightPanel = () => { const RightPanel = () => {
const activeModel = useModelStore((state) => state.activeModel); const activeModel = useModelStore((state) => state.activeModel);
const setActiveModel = useModelStore((state) => state.setActiveModel); const setActiveModel = useModelStore((state) => state.setActiveModel);
const [activeTab, setActiveTab] = useState(0);
return ( return (
<> <>
<Tabs variant={"soft-rounded"}> <Tabs variant={"enclosed"} index={activeTab}>
<TabList> <TabList>
<Tab></Tab> <Tab
<Tab></Tab> className={`${
<Tab></Tab> activeTab === 0 ? "!rounded-none bg-white shadow-lg" : ""
<Tab>LOGO</Tab> }`}
onClick={() => {
setActiveTab(() => 0);
}}
>
</Tab>
<Tab
className={`${
activeTab === 1 ? "!rounded-none bg-white shadow-lg" : ""
}`}
onClick={() => {
setActiveTab(() => 1);
}}
>
</Tab>
<Tab
className={`${
activeTab === 2 ? "!rounded-none bg-white shadow-lg" : ""
}`}
onClick={() => {
setActiveTab(() => 2);
}}
>
</Tab>
<Tab
className={`${
activeTab === 3 ? "!rounded-none bg-white shadow-lg" : ""
}`}
onClick={() => {
setActiveTab(() => 3);
}}
>
LOGO
</Tab>
</TabList> </TabList>
<TabPanels> <TabPanels>
<TabPanel> <TabPanel>
@ -33,6 +72,7 @@ const RightPanel = () => {
<img <img
className="h-full w-full object-cover" className="h-full w-full object-cover"
src={models[index].icon} src={models[index].icon}
alt={""}
/> />
</div> </div>
))} ))}
@ -41,12 +81,15 @@ const RightPanel = () => {
<TabPanel> <TabPanel>
<TextureSelectorPanel /> <TextureSelectorPanel />
</TabPanel> </TabPanel>
;
<TabPanel> <TabPanel>
<TextLabelPanel /> <TextLabelPanel />
</TabPanel> </TabPanel>
;
<TabPanel> <TabPanel>
<LogoPanel /> <LogoPanel />
</TabPanel> </TabPanel>
;
</TabPanels> </TabPanels>
</Tabs> </Tabs>
</> </>

View File

@ -1,5 +1,6 @@
import useModelStore, { DecalSticker } from "@/store/useModelStore"; import useModelStore, { DecalSticker } from "@/store/useModelStore";
import { Decal, useTexture } from "@react-three/drei"; import { Decal, useTexture } from "@react-three/drei";
// import { Euler } from "three";
/** /**
* logo 和文字标签 * logo 和文字标签
@ -16,8 +17,8 @@ const Sticker = ({ decal }: { decal: DecalSticker }) => {
// }, 10) // }, 10)
return ( return (
<Decal <Decal
// debug={true} debug={true}
position={decal.postion} position={decal.position}
scale={decal.scale} scale={decal.scale}
onPointerDown={(ev) => { onPointerDown={(ev) => {
ev.stopPropagation(); ev.stopPropagation();
@ -28,9 +29,6 @@ const Sticker = ({ decal }: { decal: DecalSticker }) => {
ev.stopPropagation(); ev.stopPropagation();
setDecalDragging(false); setDecalDragging(false);
}} }}
onPointerEnter={(_ev) => {
// ev.ctrlKey
}}
onWheel={(ev) => { onWheel={(ev) => {
ev.stopPropagation(); ev.stopPropagation();
// setDecalDragging(true); // setDecalDragging(true);
@ -47,7 +45,7 @@ const Sticker = ({ decal }: { decal: DecalSticker }) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error // @ts-expect-error
rotation={Math.PI} rotation={Math.PI}
// rotation={new Euler(Math.PI / 2, 0, 0)} // rotation={new Euler(Math.PI * euler.x, Math.PI * euler.y, Math.PI * euler.z)}
// rotation={[Math.PI * 1, Math.PI * 1, Math.PI * 1]} // rotation={[Math.PI * 1, Math.PI * 1, Math.PI * 1]}
> >
<meshPhysicalMaterial <meshPhysicalMaterial

View File

@ -67,11 +67,11 @@ const TextLabelPanel = () => {
...decals, ...decals,
{ {
id, id,
postion: new Vector3(0, 0, 0), position: new Vector3(0, 0, 0),
text, text,
url: fabricCanvas.toDataURL(), url: fabricCanvas.toDataURL(),
type: StickerType.text, type: StickerType.text,
scale: activeModel ? 1.5 : 0.05, scale: activeModel ? 1.5 : 0.5,
}, },
]); ]);
setText(() => ""); setText(() => "");

View File

@ -6,25 +6,30 @@ import { models } from "@/constant/models.ts";
import { useGLTF } from "@react-three/drei/core/useGLTF"; import { useGLTF } from "@react-three/drei/core/useGLTF";
import { textures } from "@/constant/textures.ts"; import { textures } from "@/constant/textures.ts";
import Sticker from "./Sticker"; import Sticker from "./Sticker";
import { useState, useEffect } from "react"; import { useState, useEffect, useRef } from "react";
const CapModel = () => { const CapModel = () => {
const activeModel = useModelStore((state) => state.activeModel); const activeModel = useModelStore((state) => state.activeModel);
const activeTextures = useModelStore((state) => state.activeTextures); const activeTextures = useModelStore((state) => state.activeTextures);
const decalDragging = useModelStore((state) => state.decalDragging); const decalDragging = useModelStore((state) => state.decalDragging);
const activeDecal = useModelStore((state) => state.activeDecal); const activeDecal = useModelStore((state) => state.activeDecal);
const setDecalPositon = useModelStore((state) => state.setDecalPositon); const setDecalPosition = useModelStore((state) => state.setDecalPosition);
const [isAltDown, setIsAltDown] = useState(false); const [isAltDown, setIsAltDown] = useState(false);
const ctrlRef = useRef(null);
useEffect(() => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
ctrlRef.current?.reset();
}, [activeModel]);
useEffect(() => { useEffect(() => {
function handleKeyDown(event: any) { function handleKeyDown(event: KeyboardEvent) {
if (event.key === "Alt") { if (event.key === "Alt") {
setIsAltDown(true); setIsAltDown(true);
} }
} }
function handleKeyUp(event: any) { function handleKeyUp(event: KeyboardEvent) {
if (event.key === "Alt") { if (event.key === "Alt") {
setIsAltDown(false); setIsAltDown(false);
} }
@ -42,10 +47,19 @@ const CapModel = () => {
}, []); }, []);
const { nodes } = useGLTF(models[activeModel].path); const { nodes } = useGLTF(models[activeModel].path);
// const [ry, setRy] = useState(0);
return ( return (
<> <>
<group <group
scale={models[activeModel].scale} scale={models[activeModel].scale}
onClick={(ev) => {
console.log(ev.face?.normal);
const angle = ev.face?.normal.angleTo(new Vector3(0, 1, 0));
// if (angle && angle < Math.PI) {
// angle = 2 * Math.PI - angle;
// }
console.log(angle ?? 0 / Math.PI);
}}
onPointerMove={(ev) => { onPointerMove={(ev) => {
ev.stopPropagation(); ev.stopPropagation();
if (!decalDragging) return; if (!decalDragging) return;
@ -56,7 +70,7 @@ const CapModel = () => {
y / models[activeModel].scale, y / models[activeModel].scale,
z / models[activeModel].scale, z / models[activeModel].scale,
); );
setDecalPositon(activeDecal, pos); setDecalPosition(activeDecal, pos);
} }
}} }}
> >
@ -82,15 +96,17 @@ const CapModel = () => {
const normal = ev.face?.normal; const normal = ev.face?.normal;
// normal?.transformDirection(ev.intersections[0].object.matrixWorld) // normal?.transformDirection(ev.intersections[0].object.matrixWorld)
// normal?.applyQuaternion(ev.object.quaternion) // normal?.applyQuaternion(ev.object.quaternion)
normal?.normalize(); // normal?.normalize();
console.log(normal); console.log(normal);
console.log(normal?.angleTo(new Vector3(0, 1, 0))); // const _ry = y + 0.5;
// setRy(_ry);
// console.log(normal?.angleTo(new Vector3(0, 1, 0)));
// const y_a = normal?.angleTo(new Vector3(0, 1, 0)) // const y_a = normal?.angleTo(new Vector3(0, 1, 0))
// const x_a = normal?.angleTo(new Vector3(1, 0, 0)) // const x_a = normal?.angleTo(new Vector3(1, 0, 0))
// const z_a = normal?.angleTo(new Vector3(0, 0, 1)) // const z_a = normal?.angleTo(new Vector3(0, 0, 1))
// console.log(x_a, y_a, z_a); // console.log(x_a, y_a, z_a);
setDecalPositon(activeDecal, pos); setDecalPosition(activeDecal, pos);
} }
}} }}
castShadow castShadow
@ -117,7 +133,13 @@ const CapModel = () => {
} }
})} })}
</group> </group>
<OrbitControls enabled={!decalDragging} enableZoom={!isAltDown} /> <OrbitControls
ref={ctrlRef}
makeDefault={true}
enabled={!decalDragging}
enableZoom={!isAltDown}
/>
;
</> </>
); );
}; };
@ -141,7 +163,7 @@ const ThreeScene = () => {
<pointLight position={[10, 10, 10]}></pointLight> <pointLight position={[10, 10, 10]}></pointLight>
<perspectiveCamera /> <perspectiveCamera />
<CapModel /> <CapModel />
{/* <axesHelper /> */} <axesHelper />
<Environment files={"/venice_sunset_1k.hdr"} background={false} /> <Environment files={"/venice_sunset_1k.hdr"} background={false} />
</Canvas> </Canvas>
); );

View File

@ -51,7 +51,7 @@ export const models = [
name: "版型2", name: "版型2",
icon: "/icon/model2.jpg", icon: "/icon/model2.jpg",
path: "/models/baseball_cap.glb", path: "/models/baseball_cap.glb",
scale: 0.02, scale: 0.4,
mesh: [ mesh: [
{ {
name: "baseballCap_1", name: "baseballCap_1",

View File

@ -2,6 +2,7 @@ import { create } from "zustand";
import { models } from "@/constant/models.ts"; import { models } from "@/constant/models.ts";
import { Vector3 } from "three"; import { Vector3 } from "three";
import { devtools } from "zustand/middleware"; import { devtools } from "zustand/middleware";
// import { v4 as uuidv4 } from "uuid"; // import { v4 as uuidv4 } from "uuid";
export enum StickerType { export enum StickerType {
text, text,
@ -10,7 +11,7 @@ export enum StickerType {
export interface DecalSticker { export interface DecalSticker {
id: string; id: string;
postion: Vector3; position: Vector3;
text?: string; text?: string;
url: string; url: string;
scale: number; scale: number;
@ -38,7 +39,7 @@ interface ModelState {
decals: DecalSticker[]; decals: DecalSticker[];
activeDecal?: string; activeDecal?: string;
setDecalPositon: (id: string, postion: Vector3) => void; setDecalPosition: (id: string, position: Vector3) => void;
setDecals: (decals: DecalSticker[]) => void; setDecals: (decals: DecalSticker[]) => void;
setDecalScale: (id: string, scale: number) => void; setDecalScale: (id: string, scale: number) => void;
setActiveDecal: (decalId: string) => void; setActiveDecal: (decalId: string) => void;
@ -93,13 +94,13 @@ const useModelStore = create<ModelState>()(
decals: [], decals: [],
activeDecal: undefined, activeDecal: undefined,
setDecalPositon: (id: string, postion: Vector3) => setDecalPosition: (id: string, position: Vector3) =>
set((state) => { set((state) => {
const _decals = state.decals.map((el) => { const _decals = state.decals.map((el) => {
if (el.id === id) { if (el.id === id) {
return { return {
...el, ...el,
postion, position,
}; };
} else { } else {
return el; return el;