From dbe4557dc30b994ba7be933fc0b269bfccb265d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=BF=97=E9=B9=8F?= <2916022834@qq.com> Date: Wed, 14 Jan 2026 11:26:51 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=BC=E5=87=BA=E5=9B=BE=E7=89=87=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=8D=B0=E8=8A=B1=E5=B9=B3=E9=93=BA=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/icons/CPoint.svg | 31 + .../CanvasEditor/commands/LayerCommands.js | 4 +- .../components/LayersPanel/LayersList.vue | 6 +- .../components/LayersPanel/LayersPanel.vue | 2 +- .../components/PartSelectorPanel.vue | 1187 ++++++----------- .../components/SelectMenuPanel/index.vue | 11 +- .../CanvasEditor/components/ToolsSidebar.vue | 26 +- src/component/Canvas/CanvasEditor/index.vue | 45 +- .../CanvasEditor/managers/CanvasManager.js | 65 +- .../CanvasEditor/managers/ToolManager.js | 3 +- .../Canvas/CanvasEditor/utils/layerHelper.js | 12 - src/component/Canvas/canvasExample.vue | 18 +- 12 files changed, 523 insertions(+), 887 deletions(-) create mode 100644 src/assets/icons/CPoint.svg diff --git a/src/assets/icons/CPoint.svg b/src/assets/icons/CPoint.svg new file mode 100644 index 00000000..5ae67091 --- /dev/null +++ b/src/assets/icons/CPoint.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + diff --git a/src/component/Canvas/CanvasEditor/commands/LayerCommands.js b/src/component/Canvas/CanvasEditor/commands/LayerCommands.js index 54bee584..fa01dac5 100644 --- a/src/component/Canvas/CanvasEditor/commands/LayerCommands.js +++ b/src/component/Canvas/CanvasEditor/commands/LayerCommands.js @@ -872,13 +872,13 @@ export class ToggleChildLayerVisibilityCommand extends Command { // this.oldVisibility = this.childLayer ? this.childLayer.visible : null; } - async execute() { + async execute(visible) { if (!this.childLayer) { throw new Error("找不到要切换可见性的子图层"); } // 切换可见性 - this.childLayer.visible = !this.childLayer.visible; + this.childLayer.visible = typeof visible === "boolean" ? visible : !this.childLayer.visible; // 更新画布上图层对象的可见性 if (this.canvas) { diff --git a/src/component/Canvas/CanvasEditor/components/LayersPanel/LayersList.vue b/src/component/Canvas/CanvasEditor/components/LayersPanel/LayersList.vue index e2d574e7..42433748 100644 --- a/src/component/Canvas/CanvasEditor/components/LayersPanel/LayersList.vue +++ b/src/component/Canvas/CanvasEditor/components/LayersPanel/LayersList.vue @@ -287,7 +287,7 @@ const canDeleteComputed = computed(() => { :is-child="isChild" :is-active="layer.id === activeLayerId" :is-selected="isLayerSelected(layer.id)" - :is-multi-select-mode="isMultiSelectMode && !layer.specialType" + :is-multi-select-mode="isMultiSelectMode && !(layer.isPrintTrims || layer.isPrintTrimsGroup)" :is-editing="editingLayerId === layer.id" :editing-name="editingLayerName" :can-delete=" @@ -296,7 +296,7 @@ const canDeleteComputed = computed(() => { :expanded-group-ids="expandedGroupIds" @click="(...args) => forwardEvent('layer-click', ...args)" @double-click="(...args) => forwardEvent('layer-double-click', ...args)" - @context-menu="(...args) => !layer.specialType && forwardEvent('context-menu', ...args)" + @context-menu="(...args) => !(layer.isPrintTrims || layer.isPrintTrimsGroup) && forwardEvent('context-menu', ...args)" @checkbox-change="(...args) => forwardEvent('checkbox-change', ...args)" @toggle-visibility="(...args) => forwardEvent('toggle-visibility', ...args)" @toggle-lock="(...args) => forwardEvent('toggle-lock', ...args)" @@ -337,7 +337,7 @@ const canDeleteComputed = computed(() => { :expanded-group-ids="expandedGroupIds" :isChild="true" :parentLayerId="layer.id" - :group-name="layer.specialType || groupName" + :group-name="groupName" @layer-click="(...args) => forwardEvent('layer-click', ...args)" @layer-double-click="(...args) => forwardEvent('layer-double-click', ...args)" @context-menu="(...args) => forwardEvent('context-menu', ...args)" diff --git a/src/component/Canvas/CanvasEditor/components/LayersPanel/LayersPanel.vue b/src/component/Canvas/CanvasEditor/components/LayersPanel/LayersPanel.vue index 7f8cb216..1b72e5ac 100644 --- a/src/component/Canvas/CanvasEditor/components/LayersPanel/LayersPanel.vue +++ b/src/component/Canvas/CanvasEditor/components/LayersPanel/LayersPanel.vue @@ -1242,7 +1242,7 @@ async function handleCrossLevelMove(moveData) { try { const layer = findLayerRecursively(layers.value, layerId).layer; const toLayer = findLayerRecursively(layers.value, toParentId).layer; - if(layer?.specialType || toLayer?.specialType) { + if(layer?.isPrintTrims || layer?.isPrintTrimsGroup || toLayer?.isPrintTrims || toLayer?.isPrintTrimsGroup) { console.warn("当前图层不可移动到外部"); return; } diff --git a/src/component/Canvas/CanvasEditor/components/PartSelectorPanel.vue b/src/component/Canvas/CanvasEditor/components/PartSelectorPanel.vue index 18287ea2..27632237 100644 --- a/src/component/Canvas/CanvasEditor/components/PartSelectorPanel.vue +++ b/src/component/Canvas/CanvasEditor/components/PartSelectorPanel.vue @@ -1,855 +1,436 @@ diff --git a/src/component/Canvas/CanvasEditor/components/SelectMenuPanel/index.vue b/src/component/Canvas/CanvasEditor/components/SelectMenuPanel/index.vue index f7ab1ecc..9225105f 100644 --- a/src/component/Canvas/CanvasEditor/components/SelectMenuPanel/index.vue +++ b/src/component/Canvas/CanvasEditor/components/SelectMenuPanel/index.vue @@ -178,11 +178,7 @@ import { ref, onMounted, watch, onUnmounted, reactive } from "vue"; import { useI18n } from "vue-i18n"; const { t } = useI18n(); - import { - OperationType, - SpecialLayerId, - SpecialType, - } from "../../utils/layerHelper"; + import { OperationType, SpecialLayerId } from "../../utils/layerHelper"; import { loadImageUrlToLayer } from "../../utils/imageHelper"; import { calculateRotatedTopLeftDeg, @@ -283,9 +279,6 @@ const getActiveObject = (e) => { console.log("==========切换激活对象", e, activeObjects); activeObjects.value = [...e.selected]; - // .filter((v) => - // v.specialType ? v.specialType === SpecialType.REPEAT_O : true - // );// 过滤出印花对象 activeObjects.value.forEach((v) => { v.layer = props.layerManager.getLayerById(v.layerId); }); @@ -783,7 +776,7 @@ } > .list { display: flex; - + > div { display: flex; align-items: center; diff --git a/src/component/Canvas/CanvasEditor/components/ToolsSidebar.vue b/src/component/Canvas/CanvasEditor/components/ToolsSidebar.vue index b1f29cb0..379fa38c 100644 --- a/src/component/Canvas/CanvasEditor/components/ToolsSidebar.vue +++ b/src/component/Canvas/CanvasEditor/components/ToolsSidebar.vue @@ -166,19 +166,19 @@ const normalToolsList = ref([ icon: { name: "CFont", size: "20" }, class: "text-btn", }, - { - id: OperationType.PART, - title: t("Canvas.GarmentPartSelector"), - action: () => selectTool(OperationType.PART), - icon: { name: "CPart", size: "28" }, - class: "part-btn", - activeList: [ - OperationType.PART, - OperationType.PART_RECTANGLE, - OperationType.PART_BRUSH, - OperationType.PART_ERASER, - ], - }, +// { +// id: OperationType.PART, +// title: t("Canvas.GarmentPartSelector"), +// action: () => selectTool(OperationType.PART), +// icon: { name: "CPart", size: "28" }, +// class: "part-btn", +// activeList: [ +// OperationType.PART, +// OperationType.PART_RECTANGLE, +// OperationType.PART_BRUSH, +// OperationType.PART_ERASER, +// ], +// }, { id: "help", title: t("Canvas.help"), diff --git a/src/component/Canvas/CanvasEditor/index.vue b/src/component/Canvas/CanvasEditor/index.vue index 1047f15d..58ec3023 100644 --- a/src/component/Canvas/CanvasEditor/index.vue +++ b/src/component/Canvas/CanvasEditor/index.vue @@ -1000,6 +1000,8 @@ defineExpose({ isContainBg = false, // 是否包含背景图层 isContainFixed = false, // 是否包含固定图层 isContainFixedOther = false, // 是否包含其他固定图层 + isPrintTrimsNoRepeat = true, // 是否包含印花图层的不平铺 + isPrintTrimsRepeat = true, // 是否包含印花图层的平铺 isCropByBg = false, // 是否使用背景大小裁剪 // 如果为true,则导出时裁剪到背景图层大小 layerId = "", // 导出具体图层ID layerIdArray = [], // 导出多个图层ID数组 @@ -1010,6 +1012,8 @@ defineExpose({ isContainBg, isContainFixed, isContainFixedOther, + isPrintTrimsNoRepeat, + isPrintTrimsRepeat, isCropByBg, layerId, layerIdArray, @@ -1250,7 +1254,7 @@ defineExpose({ /> - + /> --> layer.isPrintTrimsGroup)?.children || []; + for(let layer of layers){ + if(!layer.visible) continue; + let repeat = layer.fabricObjects?.[0]?.fill?.repeat || "no-repeat"; + if(typeof repeat !== "string") repeat = "no-repeat"; + if(repeat === "no-repeat"){ + if(enhancedOptions.isPrintTrimsNoRepeat) continue; + }else{ + if(enhancedOptions.isPrintTrimsRepeat) continue; + } + ptlids.push(layer.id); + const command = new ToggleChildLayerVisibilityCommand({ + canvas: this.canvas, + layers: this.layers, + layerId: layer.id, + layerManager: this.layerManager, + }); + await command.execute(false); + } + await this.changeCanvas(); + } + const res = await this.exportManager.exportImage(enhancedOptions); + // 恢复特殊图层的显示状态 + if(ptlids.length > 0){ + for(let id of ptlids){ + const command = new ToggleChildLayerVisibilityCommand({ + canvas: this.canvas, + layers: this.layers, + layerId: id, + layerManager: this.layerManager, + }); + await command.execute(true); + } + await this.changeCanvas(); + } + return res; } catch (error) { console.warn("CanvasManager导出图片失败:", error); throw error; @@ -1533,7 +1577,7 @@ export class CanvasManager { selectable: true, hasControls: true, hasBorders: true, - specialType: SpecialType.PRINT_TRIMS_O, + isPrintTrims: true, globalCompositeOperation: BlendMode.MULTIPLY, }); resolve(fabricImage); @@ -1547,7 +1591,7 @@ export class CanvasManager { visible: true, locked: false, opacity: 1.0, - specialType: SpecialType.PRINT_TRIMS_L, + isPrintTrims: true, blendMode: BlendMode.MULTIPLY, fabricObjects: [image.toObject(["id", "layerId", "layerName"])], metadata: {sourceData: item}, @@ -1601,7 +1645,7 @@ export class CanvasManager { width: image.width, height: image.height, }, - specialType: SpecialType.REPEAT_O, + isPrintTrims: true, }); this.canvas.add(rect); let layer = createLayer({ @@ -1611,7 +1655,7 @@ export class CanvasManager { visible: true, locked: true, opacity: 1, - specialType: SpecialType.REPEAT_L, + isPrintTrims: true, blendMode: BlendMode.MULTIPLY, fabricObjects: [rect.toObject(["id", "layerId", "layerName"])], metadata: {sourceData: item}, @@ -1630,7 +1674,7 @@ export class CanvasManager { // }) // children.push(layer); // } - // if(children.length === 0) return; + if(children.length === 0) return; const groupRect = new fabric.Rect({}); await this.setObjecCliptInfo(groupRect); // 插入组图层 @@ -1646,11 +1690,8 @@ export class CanvasManager { children: children, clippingMask: groupRect.toObject(), isPrintTrimsGroup: true, - specialType: SpecialType.PRINT_TRIMS_G, }); this.layers.value.splice(groupIndex, 0, groupLayer); - console.log("==========layers", [...this.layers.value]); - } /** diff --git a/src/component/Canvas/CanvasEditor/managers/ToolManager.js b/src/component/Canvas/CanvasEditor/managers/ToolManager.js index dd1c9fa5..7c0427b6 100644 --- a/src/component/Canvas/CanvasEditor/managers/ToolManager.js +++ b/src/component/Canvas/CanvasEditor/managers/ToolManager.js @@ -502,7 +502,6 @@ export class ToolManager { if (!this.canvas) return; this.canvas.isDrawingMode = false; this.canvas.selection = true; - } /** @@ -511,7 +510,7 @@ export class ToolManager { */ checkToolCanOperateSelectedObject() { const layer = this.layerManager?.getActiveLayer(); - const isSpecialLayer = !!layer?.specialType; + const isSpecialLayer = !!layer?.isPrintTrims || !!layer?.isPrintTrimsGroup; if (isSpecialLayer) { this._disableBrushIndicator(); this.canvas.defaultCursor = "not-allowed"; diff --git a/src/component/Canvas/CanvasEditor/utils/layerHelper.js b/src/component/Canvas/CanvasEditor/utils/layerHelper.js index c5884fbc..7a164fe0 100644 --- a/src/component/Canvas/CanvasEditor/utils/layerHelper.js +++ b/src/component/Canvas/CanvasEditor/utils/layerHelper.js @@ -25,18 +25,6 @@ export const SpecialLayerId = { SPECIAL_GROUP: "group_special", // 特殊组 COLOR: "special_color", // 颜色图层 } -/** - * 特殊类型 - */ -export const SpecialType = { - PRINT_TRIMS_G: "print_trims_group", // 印花和元素图层组 - PRINT_TRIMS_L: "print_trims_layer", // 印花和元素图层 - PRINT_TRIMS_O: "print_trims_object", // 印花和元素图层对象 - REPEAT_L: "repeat_layer",// 平铺图层 - REPEAT_O: "repeat_object",// 平铺图层对象 -} - - /** diff --git a/src/component/Canvas/canvasExample.vue b/src/component/Canvas/canvasExample.vue index 6c225570..4d270e12 100644 --- a/src/component/Canvas/canvasExample.vue +++ b/src/component/Canvas/canvasExample.vue @@ -335,20 +335,20 @@ 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", + designType: "Library", + path: "/src/assets/images/canvas/yinhua1.jpg", location: [550, 650], scale: [0.15, 0.2], angle: 0,