From 2fcba962d1f2b1513900f6c7118650fec1dca007 Mon Sep 17 00:00:00 2001 From: bighuixiang <472705331@qq.com> Date: Thu, 26 Jun 2025 00:37:07 +0800 Subject: [PATCH] feat(CanvasManager): enhance image layer management and event handling --- src/assets/icons/CBottom.svg | 2 +- src/assets/icons/CClear.svg | 1 + src/assets/icons/CCut.svg | 1 + .../commands/LassoCutoutCommand.js | 39 +- .../CanvasEditor/commands/RedGreenCommands.js | 6 +- .../components/LayersPanel/LayerItem.vue | 2 +- .../CanvasEditor/components/LiquifyPanel.vue | 20 +- .../components/SelectionPanel.vue | 31 +- src/component/Canvas/CanvasEditor/index.vue | 155 ++++-- .../CanvasEditor/managers/BrushIndicator.js | 467 ++++++++--------- .../CanvasEditor/managers/CanvasManager.js | 124 +++-- .../CanvasEditor/managers/LayerManager.js | 5 +- .../managers/events/CanvasEventManager.js | 8 +- .../managers/liquify/LiquifyCPUManager.js | 8 +- .../Canvas/CanvasEditor/utils/imageHelper.js | 5 + .../CanvasEditor/utils/selectionToImage.js | 475 ++++++++++++++++-- 16 files changed, 901 insertions(+), 448 deletions(-) create mode 100644 src/assets/icons/CClear.svg create mode 100644 src/assets/icons/CCut.svg diff --git a/src/assets/icons/CBottom.svg b/src/assets/icons/CBottom.svg index 635510c6..51f4715b 100644 --- a/src/assets/icons/CBottom.svg +++ b/src/assets/icons/CBottom.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/icons/CClear.svg b/src/assets/icons/CClear.svg new file mode 100644 index 00000000..4d70ef0d --- /dev/null +++ b/src/assets/icons/CClear.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/CCut.svg b/src/assets/icons/CCut.svg new file mode 100644 index 00000000..580e0926 --- /dev/null +++ b/src/assets/icons/CCut.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/component/Canvas/CanvasEditor/commands/LassoCutoutCommand.js b/src/component/Canvas/CanvasEditor/commands/LassoCutoutCommand.js index c8112643..8cc96ca7 100644 --- a/src/component/Canvas/CanvasEditor/commands/LassoCutoutCommand.js +++ b/src/component/Canvas/CanvasEditor/commands/LassoCutoutCommand.js @@ -1,4 +1,4 @@ -import { createRasterizedImage } from "../utils/SelectionToImage.js"; +import { createRasterizedImage } from "../utils/selectionToImage.js"; import { CompositeCommand } from "./Command.js"; import { CreateImageLayerCommand } from "./LayerCommands.js"; import { ClearSelectionCommand } from "./SelectionCommands.js"; @@ -63,21 +63,27 @@ export class LassoCutoutCommand extends CompositeCommand { const selectionBounds = selectionObject.getBoundingRect(true, true); // 使用createRasterizedImage执行抠图操作 - this.cutoutImageUrl = await this._performCutoutWithRasterized( + // this.cutoutImageUrl = await this._performCutoutWithRasterized( + // sourceObjects, + // selectionObject, + // selectionBounds + // ); + // if (!this.cutoutImageUrl) { + // console.error("抠图失败"); + // return false; + // } + + this.fabricImage = await this._performCutoutWithRasterized( sourceObjects, selectionObject, selectionBounds ); - if (!this.cutoutImageUrl) { - console.error("抠图失败"); - return false; - } - // 创建fabric图像对象,传递选区边界信息 - this.fabricImage = await this._createFabricImage( - this.cutoutImageUrl, - selectionBounds - ); + // // 创建fabric图像对象,传递选区边界信息 + // this.fabricImage = await this._createFabricImage( + // this.cutoutImageUrl, + // selectionBounds + // ); if (!this.fabricImage) { console.error("创建图像对象失败"); return false; @@ -224,7 +230,7 @@ export class LassoCutoutCommand extends CompositeCommand { console.log(`源对象数量: ${sourceObjects.length}`); console.log(`选区边界:`, selectionBounds); - // 确定缩放因子 + // 确定缩放因子,确保高质量输出 let scaleFactor = this.baseResolutionScale; if (this.highResolutionEnabled) { const devicePixelRatio = window.devicePixelRatio || 1; @@ -235,26 +241,27 @@ export class LassoCutoutCommand extends CompositeCommand { const rasterizedDataURL = await createRasterizedImage({ canvas: this.canvas, fabricObjects: sourceObjects, - clipPath: selectionObject, // 使用选区作为裁剪路径而不是遮罩 + clipPath: selectionObject, // 使用选区作为裁剪路径 trimWhitespace: true, trimPadding: 2, quality: 1.0, format: "png", scaleFactor: scaleFactor, - isReturenDataURL: true, // 返回DataURL + // isReturenDataURL: true, // 返回DataURL + preserveOriginalQuality: true, // 启用高质量模式 }); if (!rasterizedDataURL) { throw new Error("栅格化生成图像失败"); } - console.log(`抠图完成,缩放因子: ${scaleFactor}x`); + console.log(`✅ 高质量抠图完成,缩放因子: ${scaleFactor}x`); return rasterizedDataURL; } catch (error) { console.error("使用createRasterizedImage执行抠图失败:", error); // 如果createRasterizedImage失败,回退到原始方法 - console.log("回退到原始抠图方法..."); + console.log("⚠️ 回退到原始抠图方法..."); return await this._performCutout( { fabricObjects: sourceObjects }, selectionObject diff --git a/src/component/Canvas/CanvasEditor/commands/RedGreenCommands.js b/src/component/Canvas/CanvasEditor/commands/RedGreenCommands.js index e27eeeb4..0309755d 100644 --- a/src/component/Canvas/CanvasEditor/commands/RedGreenCommands.js +++ b/src/component/Canvas/CanvasEditor/commands/RedGreenCommands.js @@ -71,11 +71,7 @@ export class BatchInitializeRedGreenModeCommand extends Command { // 3. 保存原始状态 this.originalBackgroundObject = backgroundLayer.fabricObject ? { - ...backgroundLayer.fabricObject.toObject([ - "id", - "type", - "layerId", - ]), + ...backgroundLayer.fabricObject, ref: backgroundLayer.fabricObject, } : null; diff --git a/src/component/Canvas/CanvasEditor/components/LayersPanel/LayerItem.vue b/src/component/Canvas/CanvasEditor/components/LayersPanel/LayerItem.vue index 89273fd8..ab9df7b7 100644 --- a/src/component/Canvas/CanvasEditor/components/LayersPanel/LayerItem.vue +++ b/src/component/Canvas/CanvasEditor/components/LayersPanel/LayerItem.vue @@ -385,7 +385,7 @@ function findParentLayerId() { diff --git a/src/component/Canvas/CanvasEditor/components/LiquifyPanel.vue b/src/component/Canvas/CanvasEditor/components/LiquifyPanel.vue index e143aeff..b3589753 100644 --- a/src/component/Canvas/CanvasEditor/components/LiquifyPanel.vue +++ b/src/component/Canvas/CanvasEditor/components/LiquifyPanel.vue @@ -166,7 +166,7 @@ const props = defineProps({ // 响应式数据 const visible = ref(false); -const debug = ref(true); // 设为true可以显示调试信息 +const debug = ref(false); // 设为true可以显示调试信息 const currImage = ref(""); // 当前状态 @@ -229,11 +229,11 @@ const availableModes = ref([ { id: "push", name: "推", iconText: "↔" }, { id: "clockwise", name: "顺时针转动", iconText: "↻" }, { id: "counterclockwise", name: "逆时针转动", iconText: "↺" }, - { id: "pinch", name: "捏合", iconText: "⤢" }, - { id: "expand", name: "展开", iconText: "⤡" }, - { id: "crystal", name: "水晶", iconText: "✧" }, - { id: "edge", name: "边缘", iconText: "◈" }, - { id: "reconstruct", name: "重建", iconText: "↩" }, + // { id: "pinch", name: "捏合", iconText: "⤢" }, + // { id: "expand", name: "展开", iconText: "⤡" }, + // { id: "crystal", name: "水晶", iconText: "✧" }, + // { id: "edge", name: "边缘", iconText: "◈" }, + // { id: "reconstruct", name: "重建", iconText: "↩" }, ]); // 事件监听器引用 @@ -1535,6 +1535,7 @@ function stopPressTimer() { z-index: 1000; color: #333; border: 1px solid rgba(0, 0, 0, 0.05); + padding-bottom: 12px; } .fade-enter-active, @@ -1712,7 +1713,7 @@ function stopPressTimer() { } .mode-name { - font-size: 9px; + font-size: 14px; color: #333; text-align: center; line-height: 1.1; @@ -1774,8 +1775,9 @@ function stopPressTimer() { } .param-label { - font-size: 11px; - margin-bottom: 6px; + font-size: 12px; + margin-top: 12px; + margin-bottom: 12px; color: #666; font-weight: 500; } diff --git a/src/component/Canvas/CanvasEditor/components/SelectionPanel.vue b/src/component/Canvas/CanvasEditor/components/SelectionPanel.vue index 4ff22836..2bbb2b1b 100644 --- a/src/component/Canvas/CanvasEditor/components/SelectionPanel.vue +++ b/src/component/Canvas/CanvasEditor/components/SelectionPanel.vue @@ -47,9 +47,17 @@
- + {{ $t("拷贝并粘贴") }}
+
+ + {{ $t("剪切并粘贴") }} +
+
+ + {{ $t("清除选择内容") }} +