diff --git a/src/App.tsx b/src/App.tsx index 6758703..449f3a6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -9,14 +9,12 @@ function App() { return (
- {/**/}
- {/* */} {modelLoading !== 0 && modelLoading !== 100 && (
{ - const logo = useTexture("/textures/archlogo.png"); - const activeModel = useModelStore((state) => state.activeModel); - const activeTextures = useModelStore((state) => state.activeTextures); - const [pos, setPos] = useState(new Vector3(0, 0, 0)); - return ( - { - const { x, y, z } = ev.point; - setPos( - new Vector3( - x / models[activeModel].scale, - y / models[activeModel].scale, - z / models[activeModel].scale, - ), - ); - }} - castShadow - receiveShadow - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - geometry={mesh.geometry} - dispose={null} - > - {areaIndex > -1 ? ( - - ) : ( - - )} - - - - - ); -}; diff --git a/src/components/DebugPanel.tsx b/src/components/DebugPanel.tsx index c877474..2d93561 100644 --- a/src/components/DebugPanel.tsx +++ b/src/components/DebugPanel.tsx @@ -1,9 +1,5 @@ - - const DebugPanel = () => { - return ( -
DebugPanel
- ) -} + return
DebugPanel
; +}; -export default DebugPanel \ No newline at end of file +export default DebugPanel; diff --git a/src/components/LogoPanel.tsx b/src/components/LogoPanel.tsx index 0e2948a..368a62f 100644 --- a/src/components/LogoPanel.tsx +++ b/src/components/LogoPanel.tsx @@ -1,94 +1,108 @@ import { useState } from "react"; import { AddIcon, DeleteIcon } from "@chakra-ui/icons"; import { - Button, - Modal, - ModalBody, - ModalContent, - ModalOverlay, - Image, - IconButton + Button, + Modal, + ModalBody, + ModalContent, + ModalOverlay, + Image, + IconButton, } from "@chakra-ui/react"; import { pickFile } from "@/lib/upload"; import useModelStore, { StickerType } from "@/store/useModelStore"; -import { v4 as uuidv4 } from 'uuid' +import { v4 as uuidv4 } from "uuid"; import { Vector3 } from "three"; const LogoPanel = () => { - const [open, setOpen] = useState(false); - const decals = useModelStore((state) => state.decals); - const setDecals = useModelStore((state) => state.setDecals); - const setActiveDecal = useModelStore((state) => state.setActiveDecal); - const activeDecal = useModelStore((state) => state.activeDecal); - return ( - <> -
    - {decals - .filter((el) => el.type === StickerType.logo) - .map((decal) => ( -
  • { - setActiveDecal(decal.id) - }} - key={decal.id} - className={`mb-2 flex h-12 w-full cursor-pointer items-center justify-between rounded bg-white px-2 shadow ${decal.id === activeDecal ? "border-2 border-green-100 bg-blue-200" : "" - }`} - > - - } aria-label={""} colorScheme="red" size={"xs"} onClick={() => { - setDecals(decals.filter(el => el.id !== decal.id)) - }} /> -
  • - ))} -
-
- -
- - { - setOpen(() => false); - }} + const [open, setOpen] = useState(false); + const decals = useModelStore((state) => state.decals); + const setDecals = useModelStore((state) => state.setDecals); + const setActiveDecal = useModelStore((state) => state.setActiveDecal); + const activeModel = useModelStore((state) => state.activeModel); + const activeDecal = useModelStore((state) => state.activeDecal); + return ( + <> +
    + {decals + .filter((el) => el.type === StickerType.logo) + .map((decal) => ( +
  • { + setActiveDecal(decal.id); + }} + key={decal.id} + className={`mb-2 flex h-12 w-full cursor-pointer items-center justify-between rounded bg-white px-2 shadow ${ + decal.id === activeDecal + ? "border-2 border-green-100 bg-blue-200" + : "" + }`} > - - - -
    -
    { }} - className={ - "flex aspect-square w-24 cursor-pointer items-center justify-center rounded-lg border-2 border-gray-300" - } - > - -
    -
    -
    -
    - - - ); + + } + aria-label={""} + colorScheme="red" + size={"xs"} + onClick={() => { + setDecals(decals.filter((el) => el.id !== decal.id)); + }} + /> +
  • + ))} +
+
+ +
+ + { + setOpen(() => false); + }} + > + + + +
+
{}} + className={ + "flex aspect-square w-24 cursor-pointer items-center justify-center rounded-lg border-2 border-gray-300" + } + > + +
+
+
+
+
+ + ); }; export default LogoPanel; diff --git a/src/components/RightPanel.tsx b/src/components/RightPanel.tsx index b8a0e16..a72ff6c 100644 --- a/src/components/RightPanel.tsx +++ b/src/components/RightPanel.tsx @@ -26,8 +26,9 @@ const RightPanel = () => { onClick={() => { setActiveModel(index); }} - className={`${activeModel === index ? "border-2 border-green-300" : "" - } flex aspect-square items-center justify-center overflow-hidden rounded-xl bg-blue-100`} + className={`${ + activeModel === index ? "border-2 border-green-300" : "" + } flex aspect-square items-center justify-center overflow-hidden rounded-xl bg-blue-100`} > { - const sticker = useTexture(decal.url); - const [scale, setScale] = useState(0.05); - const setDecalDragging = useModelStore((state) => state.setDecalDragging); - const setActiveDecal = useModelStore((state) => state.setActiveDecal); - return ( - { - ev.stopPropagation(); - setActiveDecal(decal.id) - setDecalDragging(true); - }} - onPointerUp={(ev) => { - ev.stopPropagation(); - setDecalDragging(false); - }} - // onWheel={(ev) => { - // ev.stopPropagation() - // setDecalDragging(true) - // console.log(ev); - - // // setScale((state) => state += 1) - // }} - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - rotation={Math.PI} - > - - - ); + const sticker = useTexture(decal.url); + const setDecalDragging = useModelStore((state) => state.setDecalDragging); + const setDecalScale = useModelStore((state) => state.setDecalScale); + const setActiveDecal = useModelStore((state) => state.setActiveDecal); + // useInterval(() => { + // setRotation(state => state += 0.001 * Math.PI) + // }, 10) + return ( + { + ev.stopPropagation(); + setActiveDecal(decal.id); + setDecalDragging(true); + }} + onPointerUp={(ev) => { + ev.stopPropagation(); + setDecalDragging(false); + }} + onPointerEnter={(_ev) => { + // ev.ctrlKey + }} + onWheel={(ev) => { + ev.stopPropagation(); + // setDecalDragging(true); + // setDecals() + if (!ev.altKey) return; + if (ev.deltaY > 0) { + // setScale(state => state += 0.01) + setDecalScale(decal.id, (decal.scale -= 0.01)); + } else if (ev.deltaY < 0) { + setDecalScale(decal.id, (decal.scale += 0.01)); + // setScale(state => state -= 0.01) + } + }} + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + rotation={Math.PI} + // rotation={new Euler(Math.PI / 2, 0, 0)} + // rotation={[Math.PI * 1, Math.PI * 1, Math.PI * 1]} + > + + + ); }; export default Sticker; diff --git a/src/components/TextLabelPanel.tsx b/src/components/TextLabelPanel.tsx index 983f42c..31691de 100644 --- a/src/components/TextLabelPanel.tsx +++ b/src/components/TextLabelPanel.tsx @@ -1,11 +1,11 @@ import useModelStore, { StickerType } from "@/store/useModelStore"; import { AddIcon, DeleteIcon, EditIcon } from "@chakra-ui/icons"; import { - Button, - FormControl, - FormLabel, - IconButton, - Input, + Button, + FormControl, + FormLabel, + IconButton, + Input, } from "@chakra-ui/react"; import { useState } from "react"; import { fabric } from "fabric"; @@ -13,126 +13,131 @@ import { Vector3 } from "three"; import { v4 } from "uuid"; const TextLabelPanel = () => { - const decals = useModelStore((state) => state.decals); - const setDecals = useModelStore((state) => state.setDecals); - const setActiveDecal = useModelStore((state) => state.setActiveDecal); - const activeDecal = useModelStore((state) => state.activeDecal); - const [editing, setEditing] = useState(false); - const [text, setText] = useState(""); - const [color, setColor] = useState("#ffffff"); - return ( - <> - {editing ? ( -
-

添加文字

- - 文字 - { - setText(() => (ev.target as HTMLInputElement).value); - }} - /> - - - 文字颜色 - { - setColor(() => (ev.target as HTMLInputElement).value); - }} - /> - -
- -
-
- ) : ( -
    - {decals - .filter((el) => el.type === StickerType.text) - .map((text) => ( -
  • { - setActiveDecal(text.id); - }} - key={text.id} - className={`mb-2 flex h-12 w-full cursor-pointer items-center justify-between rounded bg-white px-2 shadow ${activeDecal === text.id ? "border-2 border-green-100 bg-blue-200" : "" - }`} - > - {text.text} -
    - { - ev.stopPropagation(); - }} - size={"xs"} - icon={} - aria-label={""} - /> - { - ev.stopPropagation(); - setDecals(decals.filter(el => el.id !== text.id)) - }} - size={"xs"} - icon={} - aria-label={""} - colorScheme="red" - /> -
    -
  • - ))} -
- )} -
- +
+
+ ) : ( +
    + {decals + .filter((el) => el.type === StickerType.text) + .map((text) => ( +
  • { + setActiveDecal(text.id); + }} + key={text.id} + className={`mb-2 flex h-12 w-full cursor-pointer items-center justify-between rounded bg-white px-2 shadow ${ + activeDecal === text.id + ? "border-2 border-green-100 bg-blue-200" + : "" + }`} + > + {text.text} +
    + { + ev.stopPropagation(); }} - leftIcon={} - > - 添加文字 - -
    - - ); + size={"xs"} + icon={} + aria-label={""} + /> + { + ev.stopPropagation(); + setDecals(decals.filter((el) => el.id !== text.id)); + }} + size={"xs"} + icon={} + aria-label={""} + colorScheme="red" + /> +
+ + ))} + + )} +
+ +
+ + ); }; export default TextLabelPanel; diff --git a/src/components/ThreeScene.tsx b/src/components/ThreeScene.tsx index f32274f..d6abd5a 100644 --- a/src/components/ThreeScene.tsx +++ b/src/components/ThreeScene.tsx @@ -6,7 +6,7 @@ import { models } from "@/constant/models.ts"; import { useGLTF } from "@react-three/drei/core/useGLTF"; import { textures } from "@/constant/textures.ts"; import Sticker from "./Sticker"; -import { useWhyDidYouUpdate } from "ahooks"; +import { useState, useEffect } from "react"; const CapModel = () => { const activeModel = useModelStore((state) => state.activeModel); @@ -14,9 +14,33 @@ const CapModel = () => { const decalDragging = useModelStore((state) => state.decalDragging); const activeDecal = useModelStore((state) => state.activeDecal); const setDecalPositon = useModelStore((state) => state.setDecalPositon); - useWhyDidYouUpdate('useWhyDidYouUpdateComponent', { - activeDecal, decalDragging, activeTextures, activeModel - }); + + const [isAltDown, setIsAltDown] = useState(false); + + useEffect(() => { + function handleKeyDown(event: any) { + if (event.key === "Alt") { + setIsAltDown(true); + } + } + + function handleKeyUp(event: any) { + if (event.key === "Alt") { + setIsAltDown(false); + } + } + + // 监听键盘按下和释放事件 + window.addEventListener("keydown", handleKeyDown); + window.addEventListener("keyup", handleKeyUp); + + // 组件卸载时移除事件监听器 + return () => { + window.removeEventListener("keydown", handleKeyDown); + window.removeEventListener("keyup", handleKeyUp); + }; + }, []); + const { nodes } = useGLTF(models[activeModel].path); return ( <> @@ -46,6 +70,7 @@ const CapModel = () => { key={keyName} onDoubleClick={(ev) => { ev.stopPropagation(); + if (activeDecal) { const { x, y, z } = ev.point; const pos = new Vector3( @@ -53,6 +78,18 @@ const CapModel = () => { y / models[activeModel].scale, z / models[activeModel].scale, ); + // console.log(ev.point.transformDirection(ev.intersections[0].object.matrixWorld)); + const normal = ev.face?.normal; + // normal?.transformDirection(ev.intersections[0].object.matrixWorld) + // normal?.applyQuaternion(ev.object.quaternion) + normal?.normalize(); + console.log(normal); + console.log(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 z_a = normal?.angleTo(new Vector3(0, 0, 1)) + // console.log(x_a, y_a, z_a); setDecalPositon(activeDecal, pos); } }} @@ -80,13 +117,11 @@ const CapModel = () => { } })} - + ); }; - - export const Stickers = () => { const decals = useModelStore((state) => state.decals); @@ -96,19 +131,19 @@ export const Stickers = () => { ))} - ) -} - - + ); +}; const ThreeScene = () => { + // const hdr = useTexture("/venice_sunset_1k.hdr") return ( - + + ); }; diff --git a/src/store/useModelStore.ts b/src/store/useModelStore.ts index 849f41a..031d73d 100644 --- a/src/store/useModelStore.ts +++ b/src/store/useModelStore.ts @@ -2,7 +2,7 @@ import { create } from "zustand"; import { models } from "@/constant/models.ts"; import { Vector3 } from "three"; import { devtools } from "zustand/middleware"; -import { v4 as uuidv4 } from "uuid"; +// import { v4 as uuidv4 } from "uuid"; export enum StickerType { text, logo, @@ -13,6 +13,7 @@ export interface DecalSticker { postion: Vector3; text?: string; url: string; + scale: number; type: StickerType; } @@ -32,10 +33,14 @@ interface ModelState { decalDragging: boolean; setDecalDragging: (enable: boolean) => void; + decalZooming: boolean; + setDecalZooming: (enable: boolean) => void; + decals: DecalSticker[]; activeDecal?: string; setDecalPositon: (id: string, postion: Vector3) => void; setDecals: (decals: DecalSticker[]) => void; + setDecalScale: (id: string, scale: number) => void; setActiveDecal: (decalId: string) => void; } @@ -44,7 +49,18 @@ const useModelStore = create()( modelLoading: 0, setModelLoading: (progress: number) => set(() => ({ modelLoading: progress })), - + setDecalScale: (id: string, scale: number) => + set((state) => { + const _decals = state.decals.map((el) => { + if (el.id === id) { + el.scale = scale; + } + return el; + }); + return { + decals: _decals, + }; + }), activeModel: 0, setActiveModel: (index: number) => set(() => ({ @@ -71,14 +87,11 @@ const useModelStore = create()( setDecalDragging: (enable: boolean) => set(() => ({ decalDragging: enable })), - decals: [ - { - id: uuidv4(), - url: "/textures/archlogo.png", - postion: new Vector3(0, 0, 0), - type: StickerType.logo, - }, - ], + // 是否正在缩放sticker + decalZooming: false, + setDecalZooming: (enable: boolean) => set(() => ({ decalZooming: enable })), + + decals: [], activeDecal: undefined, setDecalPositon: (id: string, postion: Vector3) => set((state) => { @@ -100,10 +113,17 @@ const useModelStore = create()( set(() => ({ decals, })), + setActiveDecal: (decalId: string) => set(() => ({ activeDecal: decalId, })), + + // // @ts-ignore + // setDecalScale: (id: string, scale: number) => + // set(() => ({ + // activeArea: 0 + // })), })), );