很多很多

This commit is contained in:
李志鹏
2026-01-15 13:42:33 +08:00
parent 9912f310ec
commit 7a4fc0736d
15 changed files with 491 additions and 984 deletions

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="201.000000pt" height="200.000000pt" viewBox="0 0 201.000000 200.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,200.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M1654 1990 c-26 -8 -153 -110 -162 -129 -1 -3 81 -89 183 -191 l185
-185 56 55 c30 30 62 67 70 82 22 41 18 123 -8 176 -29 57 -126 158 -169 176
-47 20 -118 27 -155 16z"/>
<path d="M868 1243 c-525 -527 -498 -492 -568 -725 -54 -179 -59 -219 -30
-248 19 -19 29 -21 64 -16 113 18 361 105 431 152 28 18 251 235 498 481 l447
448 -188 188 -187 187 -467 -467z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 799 B

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="200.000000pt" height="200.000000pt" viewBox="0 0 200.000000 200.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,200.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M757 1273 c-214 -214 -402 -406 -418 -427 -32 -43 -37 -82 -15 -124
23 -44 327 -338 376 -363 l45 -24 463 -3 462 -3 0 46 0 45 -342 0 -343 0 342
343 c187 188 346 352 352 364 16 32 13 74 -7 105 -9 14 -110 116 -224 227
l-207 201 -47 0 -48 0 -389 -387z m207 -342 l209 -209 -128 -131 c-76 -78
-144 -138 -169 -151 -51 -24 -106 -26 -149 -4 -41 20 -337 314 -337 334 0 13
347 370 360 370 3 0 99 -94 214 -209z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 847 B

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="200.000000pt" height="200.000000pt" viewBox="0 0 200.000000 200.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,200.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M250 1545 l0 -205 80 0 80 0 0 -340 0 -340 -80 0 -80 0 0 -205 0
-205 205 0 205 0 0 80 0 80 340 0 340 0 0 -80 0 -80 205 0 205 0 0 205 0 205
-80 0 -80 0 0 340 0 340 80 0 80 0 0 205 0 205 -205 0 -205 0 0 -80 0 -80
-340 0 -340 0 0 80 0 80 -205 0 -205 0 0 -205z m320 0 l0 -125 -120 0 -120 0
0 125 0 125 120 0 120 0 0 -125z m1100 0 l0 -125 -120 0 -120 0 0 125 0 125
120 0 120 0 0 -125z m-330 -125 l0 -80 85 0 85 0 0 -340 0 -340 -85 0 -85 0 0
-80 0 -80 -340 0 -340 0 0 80 0 80 -85 0 -85 0 0 340 0 340 85 0 85 0 0 80 0
80 340 0 340 0 0 -80z m-770 -965 l0 -125 -120 0 -120 0 0 125 0 125 120 0
120 0 0 -125z m1100 0 l0 -125 -120 0 -120 0 0 125 0 125 120 0 120 0 0 -125z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -15,6 +15,20 @@
{{ t("Canvas.GarmentPartSelector") }}
</div>
<!-- 移除关闭按钮完全通过工具切换控制显示隐藏 -->
<div class="tip">
<div>
<img
src="/src/assets/images/canvas/shubiao-l.png"
/>
<span>Left Click: Add</span>
</div>
<div>
<img
src="/src/assets/images/canvas/shubiao-r.png"
/>
<span>Right Click: Remove</span>
</div>
</div>
</div>
<div class="tool-types">
@@ -23,9 +37,9 @@
:key="item.type"
:class="[
'tool-btn',
{ active: selectionType === item.type },
{ active: toolType === item.type },
]"
@click="setSelectionType(item.type)"
@click="setPartType(item.type)"
>
<svg-icon :name="item.icon" :size="item.size" />
<span>{{ item.label }}</span>
@@ -37,24 +51,18 @@
<!-- 底部选区操作工具栏 -->
<div class="tool-actions">
<div class="action-btn" @click="copySelectionToNewLayer">
<div class="action-btn" @click="onCreate">
<svg-icon name="CPaste" size="16" />
<span class="btn-text">{{
$t("Canvas.creation")
}}</span>
</div>
<div class="action-btn" @click="cutSelectionToNewLayer">
<div class="action-btn" @click="onCopyCreate">
<svg-icon name="CCut" size="26" />
<span class="btn-text">{{
$t("Canvas.CreateAndCopy")
}}</span>
</div>
<div class="action-btn" @click="clearSelectionContent">
<svg-icon name="CClear" size="18" />
<span class="btn-text">{{
$t("Canvas.TheClearlySelectedContent")
}}</span>
</div>
</div>
</div>
</div>
@@ -90,7 +98,7 @@
type: Object,
required: true,
},
selectionManager: {
partManager: {
type: Object,
required: true,
},
@@ -115,7 +123,7 @@
// 响应式数据
const visible = ref(false);
const selectionType = ref("rectangle");
const toolType = ref(OperationType.PART);
//打开隐藏操作面板
const closePanel = ref(false);
const setClosePanel = () => {
@@ -132,20 +140,20 @@
{
type: OperationType.PART_RECTANGLE,
label: "Marquee Selection",
icon: "CRectangle",
size: "26",
icon: "CMarquee",
size: "20",
},
{
type: OperationType.PART_BRUSH,
label: "Brush Selection",
icon: "CBrush",
size: "24",
icon: "CBrush2",
size: "16",
},
{
type: OperationType.PART_ERASER,
label: "Erase",
icon: "CEraser",
size: "24",
icon: "CEraser2",
size: "22",
},
];
@@ -169,13 +177,13 @@
if (selectionTools.includes(newTool)) {
show();
// 根据工具类型设置选区类型
selectionType.value = newTool;
toolType.value = newTool;
// 更新选区管理器的选区类型
if (props.selectionManager) {
props.selectionManager.setSelectionType(selectionType.value);
props.selectionManager.setupSelectionEvents();
}
// if (props.partManager) {
// props.partManager.setPartType(toolType.value);
// props.partManager.setupPartEvents();
// }
} else {
close();
}
@@ -201,20 +209,30 @@
/**
* 设置选区类型
*/
function setSelectionType(type) {
selectionType.value = type;
function setPartType(type) {
toolType.value = type;
// 通过 ToolManager 切换工具,这会自动通知 SelectionManager
// 通过 ToolManager 切换工具,这会自动通知 partManager
if (props.toolManager) {
props.toolManager.setToolWithCommand(type);
}
// 备用方案:如果没有 toolManager直接更新 selectionManager
else if (props.selectionManager) {
props.selectionManager.setSelectionType(type);
props.selectionManager.setupSelectionEvents();
}
// // 备用方案:如果没有 toolManager直接更新 partManager
// else if (props.partManager) {
// props.partManager.setPartType(type);
// props.partManager.setupPartEvents();
// }
}
// 创建
function onCreate() {
}
// 复制并创建
function onCopyCreate() {
}
</script>
<style scoped lang="less">
@@ -290,6 +308,28 @@
// border-bottom: 1px solid rgba(0, 0, 0, 0.05);
background-color: rgba(255, 255, 255, 0.8);
border-radius: 8px 8px 0 0;
position: relative;
> .tip {
position: absolute;
top: 0;
right: 0;
display: flex;
> div {
display: flex;
align-items: center;
justify-content: center;
margin-left: 10px;
> img {
width: 12px;
height: 12px;
margin-right: 5px;
}
> span {
font-size: 10px;
color: #333;
}
}
}
}
.header-title {
@@ -370,9 +410,9 @@
.tool-actions {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-columns: repeat(2, 1fr);
gap: 5px;
padding: 0 10px;
padding: 0 30px;
}
/* 平板适配 - 每行4个按钮 */
@@ -436,5 +476,4 @@
opacity: 0.5;
cursor: not-allowed;
}
</style>

View File

@@ -385,6 +385,8 @@ onMounted(async () => {
partManager = new PartManager({
canvas: canvasManager.canvas,
layerManager,
canvasManager,
toolManager,
});
canvasManager.setPartManager(partManager);
@@ -722,8 +724,13 @@ function addRemoveBtn(fun) {
});
}
function deleteFun() {
removeLayer(layerManager.activeLayerId.value);
function deleteFun(e, control) {
const target = control.target;
if(target.onDelete){
target.onDelete(target);
}else if(target.id){
removeLayer(layerManager.activeLayerId.value);
}
}
function removeLayer(layerId) {

File diff suppressed because it is too large Load Diff

View File

@@ -197,19 +197,19 @@ export class ToolManager {
name: "部件选取工具-矩形",
icon: "part",
cursor: "default",
setup: this.setupPartTool.bind(this),
setup: this.setupPartRectangleTool.bind(this),
},
[OperationType.PART_BRUSH]: {
name: "部件选取工具-画笔",
icon: "part",
cursor: "default",
setup: this.setupPartTool.bind(this),
setup: this.setupPartBrushTool.bind(this),
},
[OperationType.PART_ERASER]: {
name: "部件选取工具-橡皮擦",
icon: "part",
cursor: "default",
setup: this.setupPartTool.bind(this),
setup: this.setupPartEraserTool.bind(this),
},
// 红绿图模式专用工具
@@ -705,14 +705,47 @@ export class ToolManager {
*/
setupPartTool() {
if (!this.canvas) return;
if (this.checkToolCanOperateSelectedObject()) return;
this.canvas.isDrawingMode = false;
this.canvas.selection = false;
if (this.canvasManager && this.canvasManager.partManager) {
this.canvasManager.partManager.setCurrentTool(OperationType.PART);
}
}
/**
* 设置部件选取工具--矩形
*/
setupPartRectangleTool() {
if (!this.canvas) return;
this.canvas.isDrawingMode = false;
this.canvas.selection = true;
if (this.canvasManager && this.canvasManager.partManager) {
this.canvasManager.partManager.setCurrentTool(OperationType.PART_RECTANGLE);
}
}
/**
* 设置部件选取工具--画笔
*/
setupPartBrushTool() {
if (!this.canvas) return;
this.canvas.isDrawingMode = true;
this.canvas.selection = false;
if (this.canvasManager && this.canvasManager.partManager) {
this.canvasManager.partManager.setCurrentTool(OperationType.PART_BRUSH);
}
}
/**
* 设置部件选取工具--橡皮擦
*/
setupPartEraserTool() {
if (!this.canvas) return;
this.canvas.isDrawingMode = false;
this.canvas.selection = false;
if (this.canvasManager && this.canvasManager.partManager) {
this.canvasManager.partManager.setCurrentTool(OperationType.PART_ERASER);
}
}
/**
* 设置波浪工具

View File

@@ -79,7 +79,9 @@
type="number"
v-model="item.object.scaleX"
step="0.1"
@input="updateList(item, 'object.scaleX', item.object.scaleX)"
@input="
updateList(item, 'object.scaleX', item.object.scaleX)
"
/>
</div>
<div>
@@ -88,7 +90,9 @@
type="number"
v-model="item.object.scaleY"
step="0.1"
@input="updateList(item, 'object.scaleY', item.object.scaleY)"
@input="
updateList(item, 'object.scaleY', item.object.scaleY)
"
/>
</div>
<div>
@@ -124,7 +128,9 @@
step="0.1"
min="0"
max="1"
@input="updateList(item, 'object.opacity', item.object.opacity)"
@input="
updateList(item, 'object.opacity', item.object.opacity)
"
/>
</div>
</div>
@@ -228,6 +234,8 @@
}
} else if (item.action === ACTIONS.SELECT) {
activeToken.value = item.token;
} else if (item.action === ACTIONS.DELETE) {
list.value = list.value.filter((v) => v.token !== item.token);
}
});
};
@@ -284,7 +292,7 @@
};
// 监听列表变化属性变更
const updateList = (item, key, value) => {
if(key === "scale[0]") item.scale[1] = value;
if (key === "scale[0]") item.scale[1] = value;
pingpuRef.value.updataList([
{
token: item.token,

View File

@@ -144,6 +144,13 @@
const list = [{ token, action: ACTIONS.SELECT }];
emit("change-canvas", list);
};
// 删除对象
const onDeleteItem = (object) => {
const list = [{ token: object.token, action: ACTIONS.DELETE }];
emit("change-canvas", list);
canvas.remove(object);
canvas.renderAll();
};
const urlToCanvas = (url) => {
return new Promise((resolve, reject) => {
fabric.Image.fromURL(
@@ -181,6 +188,7 @@
height: cheight,
fill: pattern,
...item.object,
onDelete: (v) => onDeleteItem(v),
});
canvas.add(rect);
};

View File

@@ -335,15 +335,15 @@ const otherData = {
color: {rgba: {r:255,g:0,b:0,a:1}},
printObject: {
prints: [
{
ifSingle: false,
level2Type: "Pattern",
designType: "Library",
path: "/src/assets/images/canvas/yinhua1.jpg",
location: [250, 780],
scale: [0.3, 0.4],
angle: 0,
},
// {
// ifSingle: false,
// level2Type: "Pattern",
// designType: "Library",
// path: "/src/assets/images/canvas/yinhua1.jpg",
// location: [250, 780],
// scale: [0.3, 0.4],
// angle: 0,
// },
{
ifSingle: true,
level2Type: "Pattern",

View File

@@ -5,11 +5,13 @@
</div>
</div> -->
<overall-canvas-demo />
<div style="width: 100px; height: 100px;position: relative;"><CanvasEditor /></div>
</template>
<script lang="ts" setup>
import { fabric } from "fabric-with-all";
import { ref, watch, onMounted } from "vue";
import CanvasEditor from "./CanvasEditor/index.vue";
import OverallCanvasDemo from "./OverallCanvas/demo.vue";
const imageUrl = "/src/assets/images/canvas/xiangaofenge.png";
const testRef = ref(null);