decal
This commit is contained in:
BIN
public/icon/model1.jpg
Normal file
BIN
public/icon/model1.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
public/icon/model2.jpg
Normal file
BIN
public/icon/model2.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
@ -1,12 +1,13 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { AddIcon } from "@chakra-ui/icons";
|
import { AddIcon, DeleteIcon } from "@chakra-ui/icons";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Modal,
|
Modal,
|
||||||
ModalBody,
|
ModalBody,
|
||||||
ModalContent,
|
ModalContent,
|
||||||
ModalOverlay,
|
ModalOverlay,
|
||||||
Image
|
Image,
|
||||||
|
IconButton
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { pickFile } from "@/lib/upload";
|
import { pickFile } from "@/lib/upload";
|
||||||
import useModelStore, { StickerType } from "@/store/useModelStore";
|
import useModelStore, { StickerType } from "@/store/useModelStore";
|
||||||
@ -30,10 +31,13 @@ const LogoPanel = () => {
|
|||||||
setActiveDecal(decal.id)
|
setActiveDecal(decal.id)
|
||||||
}}
|
}}
|
||||||
key={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" : ""
|
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" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Image className={"w-12 h-12 object-contain"} src={decal.url} />
|
<Image className={"w-12 h-12 object-contain"} src={decal.url} />
|
||||||
|
<IconButton icon={<DeleteIcon />} aria-label={""} colorScheme="red" size={"xs"} onClick={() => {
|
||||||
|
setDecals(decals.filter(el => el.id !== decal.id))
|
||||||
|
}} />
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -26,13 +26,12 @@ const RightPanel = () => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setActiveModel(index);
|
setActiveModel(index);
|
||||||
}}
|
}}
|
||||||
className={`${
|
className={`${activeModel === index ? "border-2 border-green-300" : ""
|
||||||
activeModel === index ? "border-2 border-green-300" : ""
|
} flex aspect-square items-center justify-center overflow-hidden rounded-xl bg-blue-100`}
|
||||||
} flex aspect-square items-center justify-center overflow-hidden rounded-xl bg-blue-100`}
|
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
className="h-full w-full object-cover"
|
className="h-full w-full object-cover"
|
||||||
src={models[activeModel].icon}
|
src={models[index].icon}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import useModelStore, { StickerType } from "@/store/useModelStore";
|
import useModelStore, { StickerType } from "@/store/useModelStore";
|
||||||
import { AddIcon, DeleteIcon, EditIcon } from "@chakra-ui/icons";
|
import { AddIcon, DeleteIcon, EditIcon } from "@chakra-ui/icons";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
FormControl,
|
FormControl,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
IconButton,
|
IconButton,
|
||||||
Input,
|
Input,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { fabric } from "fabric";
|
import { fabric } from "fabric";
|
||||||
@ -13,125 +13,126 @@ import { Vector3 } from "three";
|
|||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
|
|
||||||
const TextLabelPanel = () => {
|
const TextLabelPanel = () => {
|
||||||
const decals = useModelStore((state) => state.decals);
|
const decals = useModelStore((state) => state.decals);
|
||||||
const setDecals = useModelStore((state) => state.setDecals);
|
const setDecals = useModelStore((state) => state.setDecals);
|
||||||
const setActiveDecal = useModelStore((state) => state.setActiveDecal);
|
const setActiveDecal = useModelStore((state) => state.setActiveDecal);
|
||||||
const activeDecal = useModelStore((state) => state.activeDecal);
|
const activeDecal = useModelStore((state) => state.activeDecal);
|
||||||
const [editing, setEditing] = useState(false);
|
const [editing, setEditing] = useState(false);
|
||||||
const [text, setText] = useState("");
|
const [text, setText] = useState("");
|
||||||
const [color, setColor] = useState("#ffffff");
|
const [color, setColor] = useState("#ffffff");
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{editing ? (
|
{editing ? (
|
||||||
<div className={"rounded bg-white p-4"}>
|
<div className={"rounded bg-white p-4"}>
|
||||||
<h2 className="font-bold">添加文字</h2>
|
<h2 className="font-bold">添加文字</h2>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<FormLabel>文字</FormLabel>
|
<FormLabel>文字</FormLabel>
|
||||||
<Input
|
<Input
|
||||||
value={text}
|
value={text}
|
||||||
onInput={(ev) => {
|
onInput={(ev) => {
|
||||||
setText(() => (ev.target as HTMLInputElement).value);
|
setText(() => (ev.target as HTMLInputElement).value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<FormLabel>文字颜色</FormLabel>
|
<FormLabel>文字颜色</FormLabel>
|
||||||
<Input
|
<Input
|
||||||
type="color"
|
type="color"
|
||||||
value={color}
|
value={color}
|
||||||
onInput={(ev) => {
|
onInput={(ev) => {
|
||||||
setColor(() => (ev.target as HTMLInputElement).value);
|
setColor(() => (ev.target as HTMLInputElement).value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<div className={"mt-2 flex justify-center"}>
|
<div className={"mt-2 flex justify-center"}>
|
||||||
<Button
|
<Button
|
||||||
colorScheme="messenger"
|
colorScheme="messenger"
|
||||||
size={"sm"}
|
size={"sm"}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const canvas = document.createElement("canvas");
|
const canvas = document.createElement("canvas");
|
||||||
const fabricText = new fabric.Text(text, {
|
const fabricText = new fabric.Text(text, {
|
||||||
fill: color,
|
fill: color,
|
||||||
});
|
// fontFamily: 'sans-serif'
|
||||||
const fabricCanvas = new fabric.Canvas(canvas, {
|
});
|
||||||
width: fabricText.width,
|
const fabricCanvas = new fabric.Canvas(canvas, {
|
||||||
height: fabricText.height,
|
width: fabricText.width,
|
||||||
});
|
height: fabricText.height,
|
||||||
|
});
|
||||||
|
|
||||||
fabricCanvas.add(fabricText);
|
fabricCanvas.add(fabricText);
|
||||||
const id = v4();
|
const id = v4();
|
||||||
setActiveDecal(id);
|
setActiveDecal(id);
|
||||||
setDecals([
|
setDecals([
|
||||||
...decals,
|
...decals,
|
||||||
{
|
{
|
||||||
id,
|
id,
|
||||||
postion: new Vector3(0, 0, 0),
|
postion: new Vector3(0, 0, 0),
|
||||||
text,
|
text,
|
||||||
url: fabricCanvas.toDataURL(),
|
url: fabricCanvas.toDataURL(),
|
||||||
type: StickerType.text,
|
type: StickerType.text,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
setText(() => "");
|
setText(() => "");
|
||||||
setColor(() => "#ffffff");
|
setColor(() => "#ffffff");
|
||||||
setEditing(() => false);
|
setEditing(() => false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
确定
|
确定
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<ul>
|
|
||||||
{decals
|
|
||||||
.filter((el) => el.type === StickerType.text)
|
|
||||||
.map((text) => (
|
|
||||||
<li
|
|
||||||
onClick={() => {
|
|
||||||
setActiveDecal(text.id);
|
|
||||||
}}
|
|
||||||
key={text.id}
|
|
||||||
className={`mt-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" : ""
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<span>{text.text}</span>
|
|
||||||
<div className={"right"}>
|
|
||||||
<IconButton
|
|
||||||
onClick={(ev) => {
|
|
||||||
ev.stopPropagation();
|
|
||||||
}}
|
|
||||||
size={"xs"}
|
|
||||||
icon={<EditIcon />}
|
|
||||||
aria-label={""}
|
|
||||||
/>
|
|
||||||
<IconButton
|
|
||||||
className={"ml-1"}
|
|
||||||
onClick={(ev) => {
|
|
||||||
ev.stopPropagation();
|
|
||||||
}}
|
|
||||||
size={"xs"}
|
|
||||||
icon={<DeleteIcon />}
|
|
||||||
aria-label={""}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</li>
|
) : (
|
||||||
))}
|
<ul>
|
||||||
</ul>
|
{decals
|
||||||
)}
|
.filter((el) => el.type === StickerType.text)
|
||||||
<div className="mt-4 flex justify-center">
|
.map((text) => (
|
||||||
<Button
|
<li
|
||||||
colorScheme="messenger"
|
onClick={() => {
|
||||||
onClick={() => {
|
setActiveDecal(text.id);
|
||||||
// setOpen(() => true)
|
}}
|
||||||
setEditing(() => true);
|
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" : ""
|
||||||
leftIcon={<AddIcon />}
|
}`}
|
||||||
>
|
>
|
||||||
添加文字
|
<span>{text.text}</span>
|
||||||
</Button>
|
<div className={"right"}>
|
||||||
</div>
|
<IconButton
|
||||||
</>
|
onClick={(ev) => {
|
||||||
);
|
ev.stopPropagation();
|
||||||
|
}}
|
||||||
|
size={"xs"}
|
||||||
|
icon={<EditIcon />}
|
||||||
|
aria-label={""}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
className={"ml-1"}
|
||||||
|
onClick={(ev) => {
|
||||||
|
ev.stopPropagation();
|
||||||
|
setDecals(decals.filter(el => el.id !== text.id))
|
||||||
|
}}
|
||||||
|
size={"xs"}
|
||||||
|
icon={<DeleteIcon />}
|
||||||
|
aria-label={""}
|
||||||
|
colorScheme="red"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
<div className="mt-4 flex justify-center">
|
||||||
|
<Button
|
||||||
|
colorScheme="messenger"
|
||||||
|
onClick={() => {
|
||||||
|
setEditing(() => true);
|
||||||
|
}}
|
||||||
|
leftIcon={<AddIcon />}
|
||||||
|
>
|
||||||
|
添加文字
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TextLabelPanel;
|
export default TextLabelPanel;
|
||||||
|
@ -53,7 +53,6 @@ const CapModel = () => {
|
|||||||
y / models[activeModel].scale,
|
y / models[activeModel].scale,
|
||||||
z / models[activeModel].scale,
|
z / models[activeModel].scale,
|
||||||
);
|
);
|
||||||
console.log(pos);
|
|
||||||
setDecalPositon(activeDecal, pos);
|
setDecalPositon(activeDecal, pos);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@ -76,7 +75,6 @@ const CapModel = () => {
|
|||||||
) : (
|
) : (
|
||||||
<meshStandardMaterial color="gray" />
|
<meshStandardMaterial color="gray" />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</mesh>
|
</mesh>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ export const models = [
|
|||||||
{
|
{
|
||||||
name: "版型1",
|
name: "版型1",
|
||||||
path: "/models/baseball_cap_3d.glb",
|
path: "/models/baseball_cap_3d.glb",
|
||||||
icon: "/icon/baseball_cap_3d.png",
|
icon: "/icon/model1.jpg",
|
||||||
scale: 18,
|
scale: 18,
|
||||||
mesh: [
|
mesh: [
|
||||||
{
|
{
|
||||||
@ -49,7 +49,7 @@ export const models = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "版型2",
|
name: "版型2",
|
||||||
icon: "/icon/baseball_cap_3d_logo.png",
|
icon: "/icon/model2.jpg",
|
||||||
path: "/models/baseball_cap.glb",
|
path: "/models/baseball_cap.glb",
|
||||||
scale: 0.02,
|
scale: 0.02,
|
||||||
mesh: [
|
mesh: [
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import react from "@vitejs/plugin-react-swc";
|
import react from "@vitejs/plugin-react-swc";
|
||||||
import {fileURLToPath, URL} from 'node:url'
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
|
server: {
|
||||||
|
host: true
|
||||||
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
"@": fileURLToPath(new URL("./src", import.meta.url))
|
"@": fileURLToPath(new URL("./src", import.meta.url))
|
||||||
|
Reference in New Issue
Block a user