From ca56a184b22c6ff3478c04f81bb72c0d0f9d1049 Mon Sep 17 00:00:00 2001 From: ailanyin Date: Fri, 15 Dec 2023 17:03:07 +0800 Subject: [PATCH] right panel --- src/App.tsx | 7 +- src/components/AreaIndicator.tsx | 85 +---------------- src/components/LogoPanel.tsx | 35 +++++++ src/components/RightPanel.tsx | 19 +++- src/components/Sticker.tsx | 8 ++ src/components/TextureSelectorPanel.tsx | 35 +++++++ src/components/ThreeScene.tsx | 118 +++++++++++++++--------- src/constant/models.ts | 2 + 8 files changed, 171 insertions(+), 138 deletions(-) create mode 100644 src/components/LogoPanel.tsx create mode 100644 src/components/Sticker.tsx create mode 100644 src/components/TextureSelectorPanel.tsx diff --git a/src/App.tsx b/src/App.tsx index cca4a80..6758703 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,9 +1,5 @@ import "./App.css"; -import ThreeDesigner from "@/components/ThreeDesigner.tsx"; import { ChakraProvider, CircularProgress } from "@chakra-ui/react"; - -import AreaIndicator from "@/components/AreaIndicator.tsx"; -import TextureSelector from "@/components/TextureSelector.tsx"; import useModelStore from "@/store/useModelStore.ts"; import ThreeScene from "@/components/ThreeScene.tsx"; import RightPanel from "@/components/RightPanel.tsx"; @@ -20,8 +16,7 @@ function App() {
- - + {/* */} {modelLoading !== 0 && modelLoading !== 100 && (
{ const activeModel = useModelStore((state) => state.activeModel); - const setActiveModel = useModelStore((state) => state.setActiveModel); const activeArea = useModelStore((state) => state.activeArea); const setActiveArea = useModelStore((state) => state.setActiveArea); - const [showModelPicker, setShowModelPicker] = useState(false); return (
-
-
-
切换版型
- { - setShowModelPicker(() => true); - }} - isRound={true} - boxShadow={"lg"} - aria-label={"switch"} - icon={} - /> -
- { - setShowModelPicker(() => false); - }} - isOpen={showModelPicker} - > - -
-
- - 切换版型 - - - -
- {models.map((el, index) => ( -
{ - setActiveModel(index); - setShowModelPicker(() => false); - }} - className={`${styles.modelItem} ${ - activeModel === index ? "bg-blue-50" : "" - } flex h-10 cursor-pointer items-center rounded px-3 shadow transition-shadow hover:shadow-lg`} - key={el.path} - > - {el.name} -
- ))} -
-
- - - - - -
-
-
- - {models[activeModel].mesh.map((item, index) => ( + {models[activeModel].mesh.map((item, index) => (
{ setActiveArea(index); diff --git a/src/components/LogoPanel.tsx b/src/components/LogoPanel.tsx new file mode 100644 index 0000000..0a84f1c --- /dev/null +++ b/src/components/LogoPanel.tsx @@ -0,0 +1,35 @@ +import { useState } from "react" +import { AddIcon } from "@chakra-ui/icons" +import { Button, Modal, ModalBody, ModalContent, ModalOverlay } from "@chakra-ui/react" + +const LogoPanel = () => { + const [open, setOpen] = useState(false) + return ( + <> +
+ +
+ + { + setOpen(() => false) + }}> + + + +
+
{ + + }} className={"w-24 border-2 border-gray-300 rounded-lg aspect-square flex justify-center items-center cursor-pointer"}> + +
+
+
+
+
+ + ) +} + +export default LogoPanel \ No newline at end of file diff --git a/src/components/RightPanel.tsx b/src/components/RightPanel.tsx index e06a1ce..93ce3f5 100644 --- a/src/components/RightPanel.tsx +++ b/src/components/RightPanel.tsx @@ -1,9 +1,12 @@ import { Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react"; import { models } from "@/constant/models.ts"; import useModelStore from "@/store/useModelStore.ts"; +import TextureSelectorPanel from "./TextureSelectorPanel"; +import LogoPanel from "./LogoPanel"; const RightPanel = () => { const activeModel = useModelStore(state => state.activeModel); + const setActiveModel = useModelStore(state => state.setActiveModel); return ( <> @@ -17,13 +20,21 @@ const RightPanel = () => {
{models.map((model, index) =>
{}} - className={`${activeModel === index ? "border-green-300 border-2" : ""} flex justify-center items-center aspect-square bg-blue-100 rounded-xl`}>{model.name}
)} + onClick={() => { + setActiveModel(index) + }} + className={`${activeModel === index ? "border-green-300 border-2" : ""} flex justify-center items-center aspect-square bg-blue-100 overflow-hidden rounded-xl`}> + +
)}
- 2 + + + 3 - 4 + + + diff --git a/src/components/Sticker.tsx b/src/components/Sticker.tsx new file mode 100644 index 0000000..ad7f12e --- /dev/null +++ b/src/components/Sticker.tsx @@ -0,0 +1,8 @@ +const Sticker = () => { + return ( + <> + + ) +} + +export default Sticker \ No newline at end of file diff --git a/src/components/TextureSelectorPanel.tsx b/src/components/TextureSelectorPanel.tsx new file mode 100644 index 0000000..b323848 --- /dev/null +++ b/src/components/TextureSelectorPanel.tsx @@ -0,0 +1,35 @@ +import { models } from "@/constant/models" +import { textures } from "@/constant/textures" +import useModelStore from "@/store/useModelStore" + +function TextureSelectorPanel() { + const activeTextures = useModelStore(state => state.activeTextures) + const activeArea = useModelStore(state => state.activeArea) + const setActiveArea = useModelStore(state => state.setActiveArea) + const activeModel = useModelStore(state => state.activeModel) + const setActiveTextures = useModelStore(state => state.setActiveTextures) + return ( + <> +

选择区域

+
+ {models[activeModel].mesh.map((mesh, index) =>
{ + setActiveArea(index) + }} className={`${activeArea === index ? "border-2 border-green-300" : ""} aspect-square cursor-pointer rounded overflow-hidden`} key={mesh.name}> + +
)} +
+

选择纹理

+
+ {textures.map((texture, index) =>
{ + setActiveTextures(activeArea, index) + }} + className={`${activeTextures[activeArea] === index ? "border-2 border-green-300" : ""} rounded overflow-hidden cursor-pointer`}> + +
)} +
+ + ) +} + +export default TextureSelectorPanel \ No newline at end of file diff --git a/src/components/ThreeScene.tsx b/src/components/ThreeScene.tsx index 6d63094..a5b40e6 100644 --- a/src/components/ThreeScene.tsx +++ b/src/components/ThreeScene.tsx @@ -1,74 +1,102 @@ import { Canvas } from "@react-three/fiber"; -import { useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { TextureLoader, Vector3 } from "three"; import { Decal, Environment, OrbitControls, useTexture } from "@react-three/drei"; import useModelStore from "@/store/useModelStore.ts"; import { models } from "@/constant/models.ts"; import { useGLTF } from "@react-three/drei/core/useGLTF"; import { textures } from "@/constant/textures.ts"; +import { fabric } from 'fabric'; const CapModel = () => { const activeModel = useModelStore(state => state.activeModel); // const activeArea = useModelStore(state => state.activeArea); const activeTextures = useModelStore(state => state.activeTextures); const [pos, setPos] = useState(new Vector3(0, 0, 0)); + // const canvasRef = useRef() + const [logoUrl, setLogoUrl] = useState() const logo = useTexture("/textures/archlogo.png"); + useEffect(() => { + const canvas = document.createElement("canvas") + const fabricCanvas = new fabric.Canvas(canvas) + const text = new fabric.Text("archlinux") + fabricCanvas.add(text) + setLogoUrl(() => canvas.toDataURL()) + // canvasRef.current = canvas.toDataURL() + return () => { + // canvasRef.current = undefined + fabricCanvas.dispose() + } + }) const { nodes } = useGLTF(models[activeModel].path); return ( - - {Object.keys(nodes).map((keyName) => { - if (nodes[keyName].type === "Mesh") { - const areaIndex = models[activeModel].mesh.findIndex(el => el.name === nodes[keyName].name); - 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={nodes[keyName].geometry} - dispose={null} - > - {areaIndex > -1 ? - - : } - - - - - ); - } - })} - + <> + + {Object.keys(nodes).map((keyName) => { + if (nodes[keyName].type === "Mesh") { + const areaIndex = models[activeModel].mesh.findIndex(el => el.name === nodes[keyName].name); + return ( + { + ev.stopPropagation() + const { x, y, z } = ev.point; + const canvas = new fabric.Canvas("canvas") + 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={nodes[keyName].geometry} + dispose={null} + > + {areaIndex > -1 ? + + : } + { + ev.stopPropagation() + console.log(ev); + }} + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + rotation={Math.PI}> + + + + ); + } + })} + + + ); }; const ThreeScene = () => { + // const activeModel = useModelStore(state => state.activeModel) return ( - + {/* {activeModel > -1 && } */} diff --git a/src/constant/models.ts b/src/constant/models.ts index 502211a..a6049a6 100644 --- a/src/constant/models.ts +++ b/src/constant/models.ts @@ -2,6 +2,7 @@ export const models = [ { name: "版型1", path: "/models/baseball_cap_3d.glb", + icon: "/icon/baseball_cap_3d.png", scale: 18, mesh: [ { @@ -48,6 +49,7 @@ export const models = [ }, { name: "版型2", + icon: "/icon/baseball_cap_3d_logo.png", path: "/models/baseball_cap.glb", scale: 0.02, mesh: [