很多很多

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

@@ -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);