From c756d7377ff4d75067e4d129115b277c90b445ad Mon Sep 17 00:00:00 2001 From: bighuixiang <472705331@qq.com> Date: Mon, 21 Jul 2025 01:17:25 +0800 Subject: [PATCH] =?UTF-8?q?feat=20:=20=E6=98=BE=E7=A4=BA=E9=80=89=E5=8C=BA?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../commands/CutSelectionToNewLayerCommand.js | 118 ++-- .../commands/LassoCutoutCommand.bak.js | 160 ++++-- .../commands/LassoCutoutCommand.js | 173 ++++-- .../CanvasEditor/commands/LayerCommands.js | 513 +++++++++++++----- .../CanvasEditor/commands/StateCommands.js | 24 +- .../UpdateGroupMaskPositionCommand.js | 67 ++- .../CanvasEditor/managers/CanvasManager.js | 103 +++- .../CanvasEditor/managers/LayerManager.js | 93 +++- .../Canvas/CanvasEditor/utils/layerUtils.js | 40 +- 9 files changed, 983 insertions(+), 308 deletions(-) diff --git a/src/component/Canvas/CanvasEditor/commands/CutSelectionToNewLayerCommand.js b/src/component/Canvas/CanvasEditor/commands/CutSelectionToNewLayerCommand.js index 944e2fe9..ad5addc5 100644 --- a/src/component/Canvas/CanvasEditor/commands/CutSelectionToNewLayerCommand.js +++ b/src/component/Canvas/CanvasEditor/commands/CutSelectionToNewLayerCommand.js @@ -1,4 +1,8 @@ -import { createLayer, findInChildLayers, LayerType } from "../utils/layerHelper.js"; +import { + createLayer, + findInChildLayers, + LayerType, +} from "../utils/layerHelper.js"; import { createRasterizedImage } from "../utils/selectionToImage.js"; import { CompositeCommand, Command } from "./Command.js"; import { CreateImageLayerCommand } from "./LayerCommands.js"; @@ -52,7 +56,9 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand { const selectionObject = this.selectionManager.getSelectionObject(); if (selectionObject) { try { - this._clonedSelectionObject = await this._cloneObject(selectionObject); + this._clonedSelectionObject = await this._cloneObject( + selectionObject + ); console.log("套索抠图:选区对象已克隆保存"); } catch (error) { console.error("套索抠图:克隆选区对象失败:", error); @@ -195,6 +201,17 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand { this.groupLayer.clippingMask = clippingMask.toObject(["id", "layerId"]); // 设置组图层的fabricObject为遮罩图像 this.groupLayer.children.push(selectLayer); + + selectionObject.set({ + id: generateId("selectionObject-"), + customType: "selectionObject", + }); + + this.groupLayer.selectObject = selectionObject.toObject([ + "id", + "customType", + ]); + // 插入新组图层 this.layerManager.layers.value.splice(topLayerIndex, 1, this.groupLayer); @@ -288,7 +305,10 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand { await command.undo(); console.log(`✅ 子命令撤销成功: ${command.constructor.name}`); } catch (error) { - console.error(`❌ 子命令撤销失败: ${command.constructor.name}`, error); + console.error( + `❌ 子命令撤销失败: ${command.constructor.name}`, + error + ); // 子命令撤销失败不中断整个撤销过程 } } @@ -352,11 +372,16 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand { // 递归获取图层及其子图层的所有对象 const collectLayerObjects = (currentLayer) => { // 处理图层的fabricObjects - if (currentLayer.fabricObjects && Array.isArray(currentLayer.fabricObjects)) { + if ( + currentLayer.fabricObjects && + Array.isArray(currentLayer.fabricObjects) + ) { currentLayer.fabricObjects.forEach((fabricRef) => { if (fabricRef && fabricRef.id) { // 从画布中查找真实的对象 - const realObject = canvasObjects.find((obj) => obj.id === fabricRef.id); + const realObject = canvasObjects.find( + (obj) => obj.id === fabricRef.id + ); if (realObject && realObject.visible) { objects.push(realObject); } @@ -366,7 +391,9 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand { // 处理单个fabricObject(背景图层等) if (currentLayer.fabricObject && currentLayer.fabricObject.id) { - const realObject = canvasObjects.find((obj) => obj.id === currentLayer.fabricObject.id); + const realObject = canvasObjects.find( + (obj) => obj.id === currentLayer.fabricObject.id + ); if (realObject && realObject.visible) { objects.push(realObject); } @@ -397,7 +424,11 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand { * @returns {String} 抠图结果的DataURL * @private */ - async _performCutoutWithRasterized(sourceObjects, selectionObject, selectionBounds) { + async _performCutoutWithRasterized( + sourceObjects, + selectionObject, + selectionBounds + ) { try { console.log("=== 开始使用createRasterizedImage执行抠图 ==="); console.log(`源对象数量: ${sourceObjects.length}`); @@ -436,7 +467,10 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand { // 如果createRasterizedImage失败,回退到原始方法 console.log("⚠️ 回退到原始抠图方法..."); - return await this._performCutout({ fabricObjects: sourceObjects }, selectionObject); + return await this._performCutout( + { fabricObjects: sourceObjects }, + selectionObject + ); } } @@ -467,7 +501,9 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand { try { // 收集源图层中的可见对象 - const visibleObjects = sourceLayer.fabricObjects.filter((obj) => obj.visible); + const visibleObjects = sourceLayer.fabricObjects.filter( + (obj) => obj.visible + ); if (visibleObjects.length === 0) { throw new Error("源图层没有可见对象"); @@ -520,7 +556,10 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand { let highResolutionScale = 1; if (this.highResolutionEnabled) { const devicePixelRatio = window.devicePixelRatio || 1; - highResolutionScale = Math.max(this.baseResolutionScale, devicePixelRatio * 2); + highResolutionScale = Math.max( + this.baseResolutionScale, + devicePixelRatio * 2 + ); } // 创建用于导出的高分辨率canvas @@ -531,8 +570,12 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand { colorSpace: "srgb", }); - const actualWidth = Math.round(renderBounds.width * highResolutionScale); - const actualHeight = Math.round(renderBounds.height * highResolutionScale); + const actualWidth = Math.round( + renderBounds.width * highResolutionScale + ); + const actualHeight = Math.round( + renderBounds.height * highResolutionScale + ); exportCanvas.width = actualWidth; exportCanvas.height = actualHeight; @@ -741,14 +784,17 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand { } }); } else if (objectType === "polygon") { - fabric.Polygon.fromObject(this.serializedSelectionObject, (polygon) => { - if (polygon) { - console.log("多边形选区对象反序列化成功"); - resolve(polygon); - } else { - reject(new Error("多边形选区对象反序列化失败")); + fabric.Polygon.fromObject( + this.serializedSelectionObject, + (polygon) => { + if (polygon) { + console.log("多边形选区对象反序列化成功"); + resolve(polygon); + } else { + reject(new Error("多边形选区对象反序列化失败")); + } } - }); + ); } else if (objectType === "rect") { fabric.Rect.fromObject(this.serializedSelectionObject, (rect) => { if (rect) { @@ -759,24 +805,30 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand { } }); } else if (objectType === "ellipse" || objectType === "circle") { - fabric.Ellipse.fromObject(this.serializedSelectionObject, (ellipse) => { - if (ellipse) { - console.log("椭圆选区对象反序列化成功"); - resolve(ellipse); - } else { - reject(new Error("椭圆选区对象反序列化失败")); + fabric.Ellipse.fromObject( + this.serializedSelectionObject, + (ellipse) => { + if (ellipse) { + console.log("椭圆选区对象反序列化成功"); + resolve(ellipse); + } else { + reject(new Error("椭圆选区对象反序列化失败")); + } } - }); + ); } else { // 通用对象反序列化 - fabric.util.enlivenObjects([this.serializedSelectionObject], (objects) => { - if (objects && objects.length > 0) { - console.log("通用选区对象反序列化成功"); - resolve(objects[0]); - } else { - reject(new Error("通用选区对象反序列化失败")); + fabric.util.enlivenObjects( + [this.serializedSelectionObject], + (objects) => { + if (objects && objects.length > 0) { + console.log("通用选区对象反序列化成功"); + resolve(objects[0]); + } else { + reject(new Error("通用选区对象反序列化失败")); + } } - }); + ); } }); } catch (error) { diff --git a/src/component/Canvas/CanvasEditor/commands/LassoCutoutCommand.bak.js b/src/component/Canvas/CanvasEditor/commands/LassoCutoutCommand.bak.js index 1921d41d..8897a6bd 100644 --- a/src/component/Canvas/CanvasEditor/commands/LassoCutoutCommand.bak.js +++ b/src/component/Canvas/CanvasEditor/commands/LassoCutoutCommand.bak.js @@ -1,7 +1,14 @@ -import { createLayer, findInChildLayers, LayerType } from "../utils/layerHelper.js"; +import { + createLayer, + findInChildLayers, + LayerType, +} from "../utils/layerHelper.js"; import { createRasterizedImage } from "../utils/selectionToImage.js"; import { CompositeCommand, Command } from "./Command.js"; -import { CreateImageLayerCommand, RemoveLayerCommand } from "./LayerCommands.js"; +import { + CreateImageLayerCommand, + RemoveLayerCommand, +} from "./LayerCommands.js"; import { fabric } from "fabric-with-all"; import { generateId } from "../utils/helper.js"; @@ -58,7 +65,9 @@ export class LassoCutoutCommand extends CompositeCommand { const selectionObject = this.selectionManager.getSelectionObject(); if (selectionObject) { try { - this._clonedSelectionObject = await this._cloneObject(selectionObject); + this._clonedSelectionObject = await this._cloneObject( + selectionObject + ); console.log("套索抠图:选区对象已克隆保存"); } catch (error) { console.error("套索抠图:克隆选区对象失败:", error); @@ -94,7 +103,14 @@ export class LassoCutoutCommand extends CompositeCommand { const sourceObjects = this._getLayerObjects(activeLayer); this.originalCanvasObjects = sourceObjects; // 保存真实对象引用 this.originalFabricObjects = sourceObjects.map((obj) => - obj.toObject(["id", "layerId", "layerName", "parentId", "type", "custom"]) + obj.toObject([ + "id", + "layerId", + "layerName", + "parentId", + "type", + "custom", + ]) ); console.log( @@ -196,6 +212,7 @@ export class LassoCutoutCommand extends CompositeCommand { layers: this.layerManager.layers, layerId: this.originalLayer.id, activeLayerId: this.layerManager.activeLayerId, + layerManager: this.layerManager, }); // 执行删除原图层命令 @@ -337,7 +354,10 @@ export class LassoCutoutCommand extends CompositeCommand { await command.undo(); console.log(`✅ 子命令撤销成功: ${command.constructor.name}`); } catch (error) { - console.error(`❌ 子命令撤销失败: ${command.constructor.name}`, error); + console.error( + `❌ 子命令撤销失败: ${command.constructor.name}`, + error + ); // 子命令撤销失败不中断整个撤销过程 } } @@ -414,11 +434,16 @@ export class LassoCutoutCommand extends CompositeCommand { // 递归获取图层及其子图层的所有对象 const collectLayerObjects = (currentLayer) => { // 处理图层的fabricObjects - if (currentLayer.fabricObjects && Array.isArray(currentLayer.fabricObjects)) { + if ( + currentLayer.fabricObjects && + Array.isArray(currentLayer.fabricObjects) + ) { currentLayer.fabricObjects.forEach((fabricRef) => { if (fabricRef && fabricRef.id) { // 从画布中查找真实的对象 - const realObject = canvasObjects.find((obj) => obj.id === fabricRef.id); + const realObject = canvasObjects.find( + (obj) => obj.id === fabricRef.id + ); if (realObject && realObject.visible) { objects.push(realObject); } @@ -428,7 +453,9 @@ export class LassoCutoutCommand extends CompositeCommand { // 处理单个fabricObject(背景图层等) if (currentLayer.fabricObject && currentLayer.fabricObject.id) { - const realObject = canvasObjects.find((obj) => obj.id === currentLayer.fabricObject.id); + const realObject = canvasObjects.find( + (obj) => obj.id === currentLayer.fabricObject.id + ); if (realObject && realObject.visible) { objects.push(realObject); } @@ -459,7 +486,11 @@ export class LassoCutoutCommand extends CompositeCommand { * @returns {String} 抠图结果的DataURL * @private */ - async _performCutoutWithRasterized(sourceObjects, selectionObject, selectionBounds) { + async _performCutoutWithRasterized( + sourceObjects, + selectionObject, + selectionBounds + ) { try { console.log("=== 开始使用createRasterizedImage执行抠图 ==="); console.log(`源对象数量: ${sourceObjects.length}`); @@ -498,7 +529,10 @@ export class LassoCutoutCommand extends CompositeCommand { // 如果createRasterizedImage失败,回退到原始方法 console.log("⚠️ 回退到原始抠图方法..."); - return await this._performCutout({ fabricObjects: sourceObjects }, selectionObject); + return await this._performCutout( + { fabricObjects: sourceObjects }, + selectionObject + ); } } @@ -529,7 +563,9 @@ export class LassoCutoutCommand extends CompositeCommand { try { // 收集源图层中的可见对象 - const visibleObjects = sourceLayer.fabricObjects.filter((obj) => obj.visible); + const visibleObjects = sourceLayer.fabricObjects.filter( + (obj) => obj.visible + ); if (visibleObjects.length === 0) { throw new Error("源图层没有可见对象"); @@ -582,7 +618,10 @@ export class LassoCutoutCommand extends CompositeCommand { let highResolutionScale = 1; if (this.highResolutionEnabled) { const devicePixelRatio = window.devicePixelRatio || 1; - highResolutionScale = Math.max(this.baseResolutionScale, devicePixelRatio * 2); + highResolutionScale = Math.max( + this.baseResolutionScale, + devicePixelRatio * 2 + ); } // 创建用于导出的高分辨率canvas @@ -593,8 +632,12 @@ export class LassoCutoutCommand extends CompositeCommand { colorSpace: "srgb", }); - const actualWidth = Math.round(renderBounds.width * highResolutionScale); - const actualHeight = Math.round(renderBounds.height * highResolutionScale); + const actualWidth = Math.round( + renderBounds.width * highResolutionScale + ); + const actualHeight = Math.round( + renderBounds.height * highResolutionScale + ); exportCanvas.width = actualWidth; exportCanvas.height = actualHeight; @@ -803,14 +846,17 @@ export class LassoCutoutCommand extends CompositeCommand { } }); } else if (objectType === "polygon") { - fabric.Polygon.fromObject(this.serializedSelectionObject, (polygon) => { - if (polygon) { - console.log("多边形选区对象反序列化成功"); - resolve(polygon); - } else { - reject(new Error("多边形选区对象反序列化失败")); + fabric.Polygon.fromObject( + this.serializedSelectionObject, + (polygon) => { + if (polygon) { + console.log("多边形选区对象反序列化成功"); + resolve(polygon); + } else { + reject(new Error("多边形选区对象反序列化失败")); + } } - }); + ); } else if (objectType === "rect") { fabric.Rect.fromObject(this.serializedSelectionObject, (rect) => { if (rect) { @@ -821,24 +867,30 @@ export class LassoCutoutCommand extends CompositeCommand { } }); } else if (objectType === "ellipse" || objectType === "circle") { - fabric.Ellipse.fromObject(this.serializedSelectionObject, (ellipse) => { - if (ellipse) { - console.log("椭圆选区对象反序列化成功"); - resolve(ellipse); - } else { - reject(new Error("椭圆选区对象反序列化失败")); + fabric.Ellipse.fromObject( + this.serializedSelectionObject, + (ellipse) => { + if (ellipse) { + console.log("椭圆选区对象反序列化成功"); + resolve(ellipse); + } else { + reject(new Error("椭圆选区对象反序列化失败")); + } } - }); + ); } else { // 通用对象反序列化 - fabric.util.enlivenObjects([this.serializedSelectionObject], (objects) => { - if (objects && objects.length > 0) { - console.log("通用选区对象反序列化成功"); - resolve(objects[0]); - } else { - reject(new Error("通用选区对象反序列化失败")); + fabric.util.enlivenObjects( + [this.serializedSelectionObject], + (objects) => { + if (objects && objects.length > 0) { + console.log("通用选区对象反序列化成功"); + resolve(objects[0]); + } else { + reject(new Error("通用选区对象反序列化失败")); + } } - }); + ); } }); } catch (error) { @@ -862,7 +914,11 @@ export class LassoCutoutCommand extends CompositeCommand { // 1. 恢复图层到原位置 if (this.originalLayerIndex !== -1) { - this.layerManager.layers.value.splice(this.originalLayerIndex, 0, this.originalLayer); + this.layerManager.layers.value.splice( + this.originalLayerIndex, + 0, + this.originalLayer + ); } else { // 如果没有保存索引,添加到末尾 this.layerManager.layers.value.push(this.originalLayer); @@ -870,7 +926,9 @@ export class LassoCutoutCommand extends CompositeCommand { // 2. 恢复fabric对象到画布 if (this.originalFabricObjects.length > 0) { - console.log(`↩️ 恢复 ${this.originalFabricObjects.length} 个fabric对象`); + console.log( + `↩️ 恢复 ${this.originalFabricObjects.length} 个fabric对象` + ); // 使用fabric.util.enlivenObjects批量反序列化对象 await new Promise((resolve, reject) => { @@ -1019,7 +1077,12 @@ export class ClearSelectionCommand extends Command { // 序列化选区对象和相关状态 this.originalSelectionState = { // 选区对象的序列化数据 - selectionObjectData: selectionObject.toObject(["id", "layerId", "layerName", "parentId"]), + selectionObjectData: selectionObject.toObject([ + "id", + "layerId", + "layerName", + "parentId", + ]), // 选区路径数据 selectionPath: this.selectionManager.getSelectionPath(), // 羽化值 @@ -1082,8 +1145,13 @@ export class ClearSelectionCommand extends Command { return; } - const { selectionObjectData, featherAmount, selectionStyle, position, managerState } = - this.originalSelectionState; + const { + selectionObjectData, + featherAmount, + selectionStyle, + position, + managerState, + } = this.originalSelectionState; // 根据选区对象类型进行反序列化 const objectType = selectionObjectData.type; @@ -1129,7 +1197,9 @@ export class ClearSelectionCommand extends Command { // 恢复阴影(羽化效果) if (selectionStyle.shadow) { - restoredObject.shadow = new fabric.Shadow(selectionStyle.shadow); + restoredObject.shadow = new fabric.Shadow( + selectionStyle.shadow + ); } } @@ -1214,7 +1284,9 @@ export class ClearSelectionCommand extends Command { // 验证基本属性 const originalId = this.originalSelectionState.selectionId; if (currentSelection.id !== originalId) { - console.warn(`选区ID不匹配: 期望 ${originalId}, 实际 ${currentSelection.id}`); + console.warn( + `选区ID不匹配: 期望 ${originalId}, 实际 ${currentSelection.id}` + ); return false; } @@ -1222,7 +1294,9 @@ export class ClearSelectionCommand extends Command { const currentFeather = this.selectionManager.getFeatherAmount(); const originalFeather = this.originalSelectionState.featherAmount; if (currentFeather !== originalFeather) { - console.warn(`羽化值不匹配: 期望 ${originalFeather}, 实际 ${currentFeather}`); + console.warn( + `羽化值不匹配: 期望 ${originalFeather}, 实际 ${currentFeather}` + ); return false; } diff --git a/src/component/Canvas/CanvasEditor/commands/LassoCutoutCommand.js b/src/component/Canvas/CanvasEditor/commands/LassoCutoutCommand.js index e79b69df..06a3ade1 100644 --- a/src/component/Canvas/CanvasEditor/commands/LassoCutoutCommand.js +++ b/src/component/Canvas/CanvasEditor/commands/LassoCutoutCommand.js @@ -1,7 +1,15 @@ -import { createLayer, findInChildLayers, LayerType, OperationType } from "../utils/layerHelper.js"; +import { + createLayer, + findInChildLayers, + LayerType, + OperationType, +} from "../utils/layerHelper.js"; import { createRasterizedImage } from "../utils/selectionToImage.js"; import { CompositeCommand, Command } from "./Command.js"; -import { CreateImageLayerCommand, RemoveLayerCommand } from "./LayerCommands.js"; +import { + CreateImageLayerCommand, + RemoveLayerCommand, +} from "./LayerCommands.js"; import { fabric } from "fabric-with-all"; import { generateId } from "../utils/helper.js"; import { ToolCommand } from "./ToolCommands.js"; @@ -61,7 +69,9 @@ export class LassoCutoutCommand extends CompositeCommand { const selectionObject = this.selectionManager.getSelectionObject(); if (selectionObject) { try { - this._clonedSelectionObject = await this._cloneObject(selectionObject); + this._clonedSelectionObject = await this._cloneObject( + selectionObject + ); console.log("套索抠图:选区对象已克隆保存"); } catch (error) { console.error("套索抠图:克隆选区对象失败:", error); @@ -97,7 +107,14 @@ export class LassoCutoutCommand extends CompositeCommand { const sourceObjects = this._getLayerObjects(activeLayer); this.originalCanvasObjects = sourceObjects; // 保存真实对象引用 this.originalFabricObjects = sourceObjects.map((obj) => - obj.toObject(["id", "layerId", "layerName", "parentId", "type", "custom"]) + obj.toObject([ + "id", + "layerId", + "layerName", + "parentId", + "type", + "custom", + ]) ); console.log( @@ -245,7 +262,8 @@ export class LassoCutoutCommand extends CompositeCommand { // canvas: this.canvas, // layers: this.layerManager.layers, // layerId: this.originalLayer.id, - // activeLayerId: this.layerManager.activeLayerId,l + // activeLayerId: this.layerManager.activeLayerId, + // layerManager: this.layerManager, // }); // // 执行删除原图层命令 @@ -254,6 +272,16 @@ export class LassoCutoutCommand extends CompositeCommand { this.groupLayer.clippingMask = clippingMask.toObject(["id", "layerId"]); // 设置组图层的fabricObject为遮罩图像 + selectionObject.set({ + id: generateId("selectionObject-"), + customType: "selectionObject", + }); + + this.groupLayer.selectObject = selectionObject.toObject([ + "id", + "customType", + ]); + this.groupLayer.children.push(selectLayer); // 插入新组图层 this.layerManager.layers.value.splice(topLayerIndex, 0, this.groupLayer); @@ -368,7 +396,10 @@ export class LassoCutoutCommand extends CompositeCommand { await command.undo(); console.log(`✅ 子命令撤销成功: ${command.constructor.name}`); } catch (error) { - console.error(`❌ 子命令撤销失败: ${command.constructor.name}`, error); + console.error( + `❌ 子命令撤销失败: ${command.constructor.name}`, + error + ); // 子命令撤销失败不中断整个撤销过程 } } @@ -445,11 +476,16 @@ export class LassoCutoutCommand extends CompositeCommand { // 递归获取图层及其子图层的所有对象 const collectLayerObjects = (currentLayer) => { // 处理图层的fabricObjects - if (currentLayer.fabricObjects && Array.isArray(currentLayer.fabricObjects)) { + if ( + currentLayer.fabricObjects && + Array.isArray(currentLayer.fabricObjects) + ) { currentLayer.fabricObjects.forEach((fabricRef) => { if (fabricRef && fabricRef.id) { // 从画布中查找真实的对象 - const realObject = canvasObjects.find((obj) => obj.id === fabricRef.id); + const realObject = canvasObjects.find( + (obj) => obj.id === fabricRef.id + ); if (realObject && realObject.visible) { objects.push(realObject); } @@ -459,7 +495,9 @@ export class LassoCutoutCommand extends CompositeCommand { // 处理单个fabricObject(背景图层等) if (currentLayer.fabricObject && currentLayer.fabricObject.id) { - const realObject = canvasObjects.find((obj) => obj.id === currentLayer.fabricObject.id); + const realObject = canvasObjects.find( + (obj) => obj.id === currentLayer.fabricObject.id + ); if (realObject && realObject.visible) { objects.push(realObject); } @@ -490,7 +528,11 @@ export class LassoCutoutCommand extends CompositeCommand { * @returns {String} 抠图结果的DataURL * @private */ - async _performCutoutWithRasterized(sourceObjects, selectionObject, selectionBounds) { + async _performCutoutWithRasterized( + sourceObjects, + selectionObject, + selectionBounds + ) { try { console.log("=== 开始使用createRasterizedImage执行抠图 ==="); console.log(`源对象数量: ${sourceObjects.length}`); @@ -529,7 +571,10 @@ export class LassoCutoutCommand extends CompositeCommand { // 如果createRasterizedImage失败,回退到原始方法 console.log("⚠️ 回退到原始抠图方法..."); - return await this._performCutout({ fabricObjects: sourceObjects }, selectionObject); + return await this._performCutout( + { fabricObjects: sourceObjects }, + selectionObject + ); } } @@ -560,7 +605,9 @@ export class LassoCutoutCommand extends CompositeCommand { try { // 收集源图层中的可见对象 - const visibleObjects = sourceLayer.fabricObjects.filter((obj) => obj.visible); + const visibleObjects = sourceLayer.fabricObjects.filter( + (obj) => obj.visible + ); if (visibleObjects.length === 0) { throw new Error("源图层没有可见对象"); @@ -613,7 +660,10 @@ export class LassoCutoutCommand extends CompositeCommand { let highResolutionScale = 1; if (this.highResolutionEnabled) { const devicePixelRatio = window.devicePixelRatio || 1; - highResolutionScale = Math.max(this.baseResolutionScale, devicePixelRatio * 2); + highResolutionScale = Math.max( + this.baseResolutionScale, + devicePixelRatio * 2 + ); } // 创建用于导出的高分辨率canvas @@ -624,8 +674,12 @@ export class LassoCutoutCommand extends CompositeCommand { colorSpace: "srgb", }); - const actualWidth = Math.round(renderBounds.width * highResolutionScale); - const actualHeight = Math.round(renderBounds.height * highResolutionScale); + const actualWidth = Math.round( + renderBounds.width * highResolutionScale + ); + const actualHeight = Math.round( + renderBounds.height * highResolutionScale + ); exportCanvas.width = actualWidth; exportCanvas.height = actualHeight; @@ -834,14 +888,17 @@ export class LassoCutoutCommand extends CompositeCommand { } }); } else if (objectType === "polygon") { - fabric.Polygon.fromObject(this.serializedSelectionObject, (polygon) => { - if (polygon) { - console.log("多边形选区对象反序列化成功"); - resolve(polygon); - } else { - reject(new Error("多边形选区对象反序列化失败")); + fabric.Polygon.fromObject( + this.serializedSelectionObject, + (polygon) => { + if (polygon) { + console.log("多边形选区对象反序列化成功"); + resolve(polygon); + } else { + reject(new Error("多边形选区对象反序列化失败")); + } } - }); + ); } else if (objectType === "rect") { fabric.Rect.fromObject(this.serializedSelectionObject, (rect) => { if (rect) { @@ -852,24 +909,30 @@ export class LassoCutoutCommand extends CompositeCommand { } }); } else if (objectType === "ellipse" || objectType === "circle") { - fabric.Ellipse.fromObject(this.serializedSelectionObject, (ellipse) => { - if (ellipse) { - console.log("椭圆选区对象反序列化成功"); - resolve(ellipse); - } else { - reject(new Error("椭圆选区对象反序列化失败")); + fabric.Ellipse.fromObject( + this.serializedSelectionObject, + (ellipse) => { + if (ellipse) { + console.log("椭圆选区对象反序列化成功"); + resolve(ellipse); + } else { + reject(new Error("椭圆选区对象反序列化失败")); + } } - }); + ); } else { // 通用对象反序列化 - fabric.util.enlivenObjects([this.serializedSelectionObject], (objects) => { - if (objects && objects.length > 0) { - console.log("通用选区对象反序列化成功"); - resolve(objects[0]); - } else { - reject(new Error("通用选区对象反序列化失败")); + fabric.util.enlivenObjects( + [this.serializedSelectionObject], + (objects) => { + if (objects && objects.length > 0) { + console.log("通用选区对象反序列化成功"); + resolve(objects[0]); + } else { + reject(new Error("通用选区对象反序列化失败")); + } } - }); + ); } }); } catch (error) { @@ -893,7 +956,11 @@ export class LassoCutoutCommand extends CompositeCommand { // 1. 恢复图层到原位置 if (this.originalLayerIndex !== -1) { - this.layerManager.layers.value.splice(this.originalLayerIndex, 0, this.originalLayer); + this.layerManager.layers.value.splice( + this.originalLayerIndex, + 0, + this.originalLayer + ); } else { // 如果没有保存索引,添加到末尾 this.layerManager.layers.value.push(this.originalLayer); @@ -901,7 +968,9 @@ export class LassoCutoutCommand extends CompositeCommand { // 2. 恢复fabric对象到画布 if (this.originalFabricObjects.length > 0) { - console.log(`↩️ 恢复 ${this.originalFabricObjects.length} 个fabric对象`); + console.log( + `↩️ 恢复 ${this.originalFabricObjects.length} 个fabric对象` + ); // 使用fabric.util.enlivenObjects批量反序列化对象 await new Promise((resolve, reject) => { @@ -1050,7 +1119,12 @@ export class ClearSelectionCommand extends Command { // 序列化选区对象和相关状态 this.originalSelectionState = { // 选区对象的序列化数据 - selectionObjectData: selectionObject.toObject(["id", "layerId", "layerName", "parentId"]), + selectionObjectData: selectionObject.toObject([ + "id", + "layerId", + "layerName", + "parentId", + ]), // 选区路径数据 selectionPath: this.selectionManager.getSelectionPath(), // 羽化值 @@ -1113,8 +1187,13 @@ export class ClearSelectionCommand extends Command { return; } - const { selectionObjectData, featherAmount, selectionStyle, position, managerState } = - this.originalSelectionState; + const { + selectionObjectData, + featherAmount, + selectionStyle, + position, + managerState, + } = this.originalSelectionState; // 根据选区对象类型进行反序列化 const objectType = selectionObjectData.type; @@ -1160,7 +1239,9 @@ export class ClearSelectionCommand extends Command { // 恢复阴影(羽化效果) if (selectionStyle.shadow) { - restoredObject.shadow = new fabric.Shadow(selectionStyle.shadow); + restoredObject.shadow = new fabric.Shadow( + selectionStyle.shadow + ); } } @@ -1245,7 +1326,9 @@ export class ClearSelectionCommand extends Command { // 验证基本属性 const originalId = this.originalSelectionState.selectionId; if (currentSelection.id !== originalId) { - console.warn(`选区ID不匹配: 期望 ${originalId}, 实际 ${currentSelection.id}`); + console.warn( + `选区ID不匹配: 期望 ${originalId}, 实际 ${currentSelection.id}` + ); return false; } @@ -1253,7 +1336,9 @@ export class ClearSelectionCommand extends Command { const currentFeather = this.selectionManager.getFeatherAmount(); const originalFeather = this.originalSelectionState.featherAmount; if (currentFeather !== originalFeather) { - console.warn(`羽化值不匹配: 期望 ${originalFeather}, 实际 ${currentFeather}`); + console.warn( + `羽化值不匹配: 期望 ${originalFeather}, 实际 ${currentFeather}` + ); return false; } diff --git a/src/component/Canvas/CanvasEditor/commands/LayerCommands.js b/src/component/Canvas/CanvasEditor/commands/LayerCommands.js index 95572291..85523f50 100644 --- a/src/component/Canvas/CanvasEditor/commands/LayerCommands.js +++ b/src/component/Canvas/CanvasEditor/commands/LayerCommands.js @@ -1,5 +1,10 @@ import { Command } from "./Command"; -import { createLayer, findLayerRecursively, LayerType, OperationType } from "../utils/layerHelper"; +import { + createLayer, + findLayerRecursively, + LayerType, + OperationType, +} from "../utils/layerHelper"; import { createStaticCanvas } from "../utils/canvasFactory"; import { AddObjectToLayerCommand } from "./ObjectLayerCommands"; import { ToolCommand } from "./ToolCommands"; @@ -110,11 +115,15 @@ export class AddLayerCommand extends Command { parentLayer.children = parentLayer.children || []; parentLayer.children.splice(insertIndex, 0, newLayer); - console.log(`新图层已插入到子图层位置: ${insertIndex} (父图层: ${parentLayer.name})`); + console.log( + `新图层已插入到子图层位置: ${insertIndex} (父图层: ${parentLayer.name})` + ); } else { // 当前激活图层是一级图层 // 在一级图层中,插入到激活图层之上 - const activeLayerIndex = layers.findIndex((layer) => layer.id === currentActiveLayerId); + const activeLayerIndex = layers.findIndex( + (layer) => layer.id === currentActiveLayerId + ); insertIndex = Math.max(0, activeLayerIndex); layers.splice(insertIndex, 0, newLayer); @@ -215,11 +224,18 @@ export class PasteLayerCommand extends Command { if (this.isGroupLayer) { // 粘贴为新的组图层,递归生成子图层ID - const { groupLayer, allObjects } = await this._createGroupLayerFromClipboard(data); + const { groupLayer, allObjects } = + await this._createGroupLayerFromClipboard(data); if (groupLayer?.clippingMask) { // 给遮罩蒙层也添加上偏移 groupLayer.clippingMask.left += 30; groupLayer.clippingMask.top += 30; + + if (groupLayer.selectObject) { + // 如果有选区 则选区位置也要更新 + groupLayer.selectObject.left = groupLayer.clippingMask.left; + groupLayer.selectObject.top = groupLayer.clippingMask.top; + } } this.newLayer = groupLayer; this.newLayer.id = this.newLayerId; @@ -232,7 +248,8 @@ export class PasteLayerCommand extends Command { } // 重新创建遮罩对象 const clippingMaskFabricObject = - (await restoreFabricObject(groupLayer?.clippingMask, this.canvas)) || null; + (await restoreFabricObject(groupLayer?.clippingMask, this.canvas)) || + null; clippingMaskFabricObject.clipPath = null; clippingMaskFabricObject.set({ @@ -411,7 +428,9 @@ export class PasteLayerCommand extends Command { if (obj.top !== undefined) obj.top += offset; } this.canvas.add(obj); - this.newLayer.fabricObjects.push(obj.toObject(["id", "layerId", "layerName"])); + this.newLayer.fabricObjects.push( + obj.toObject(["id", "layerId", "layerName"]) + ); this.createdObjects.push(obj); }); this._onObjectsRestored(data); @@ -436,7 +455,10 @@ export class PasteLayerCommand extends Command { async undo() { if (!this.newLayer || !this.newLayerId) return; - const { layer, parent } = findLayerRecursively(this.layers.value, this.newLayerId); + const { layer, parent } = findLayerRecursively( + this.layers.value, + this.newLayerId + ); if (!layer) { console.error(`图层 ${this.newLayerId} 不存在, 无法撤销`); return false; @@ -444,13 +466,17 @@ export class PasteLayerCommand extends Command { if (parent) { // 如果是子图层,直接从父图层中删除 - const index = parent.children.findIndex((child) => child.id === this.newLayerId); + const index = parent.children.findIndex( + (child) => child.id === this.newLayerId + ); if (index !== -1) { parent.children.splice(index, 1); } } else { // 如果是顶级图层,直接从图层列表中删除 - const index = this.layers.value.findIndex((l) => l.id === this.newLayerId); + const index = this.layers.value.findIndex( + (l) => l.id === this.newLayerId + ); if (index !== -1) { this.layers.value.splice(index, 1); } @@ -496,9 +522,12 @@ export class RemoveLayerCommand extends Command { this.layers = options.layers; this.layerId = options.layerId; this.activeLayerId = options.activeLayerId; + this.layerManager = options.layerManager || null; // 查找要删除的图层 - this.layerIndex = this.layers.value.findIndex((layer) => layer.id === this.layerId); + this.layerIndex = this.layers.value.findIndex( + (layer) => layer.id === this.layerId + ); this.removedLayer = this.layers.value[this.layerIndex]; this.isActiveLayer = this.layerId === this.activeLayerId.value; @@ -546,7 +575,7 @@ export class RemoveLayerCommand extends Command { return allObjects; } - execute() { + async execute() { if (this.layerIndex === -1 || !this.removedLayer) { console.error(`图层 ${this.layerId} 不存在`); return false; @@ -565,7 +594,9 @@ export class RemoveLayerCommand extends Command { // 如果删除的是当前活动图层,需要更新活动图层 if (this.isActiveLayer) { // 查找最近的非背景层作为新的活动图层 - const newActiveLayer = this.layers.value.find((layer) => !layer.isBackground); + const newActiveLayer = this.layers.value.find( + (layer) => !layer.isBackground + ); if (newActiveLayer) { this.activeLayerId.value = newActiveLayer.id; } else { @@ -574,9 +605,11 @@ export class RemoveLayerCommand extends Command { } // 重新渲染画布 - if (this.canvas) { - this.canvas.renderAll(); - } + // if (this.canvas) { + // this.canvas.renderAll(); + // } + await this.layerManager?.updateLayersObjectsInteractivity?.(); + // this.canvas.renderAll(); console.log( `✅ 已移除图层: ${this.removedLayer.name} (ID: ${this.layerId}),包含 ${this.originalObjects.length} 个对象` @@ -584,13 +617,14 @@ export class RemoveLayerCommand extends Command { return true; } - undo() { + async undo() { // 恢复图层到原位置 if (this.layerIndex !== -1 && this.removedLayer) { this.layers.value.splice(this.layerIndex, 0, this.removedLayer); + debugger; // 使用优化渲染批处理恢复真实对象到画布 - optimizeCanvasRendering(this.canvas, () => { + await optimizeCanvasRendering(this.canvas, () => { // 倒序添加对象,确保下标越小的子图层在画布中越靠前 this.originalObjects .slice() @@ -605,6 +639,9 @@ export class RemoveLayerCommand extends Command { }); }); + await this.layerManager?.updateLayersObjectsInteractivity?.(); + this.canvas.renderAll(); + // 如果删除的是当前活动图层,恢复活动图层 if (this.isActiveLayer) { this.activeLayerId.value = this.layerId; @@ -643,22 +680,30 @@ export class MoveLayerCommand extends Command { this.parentLayer = null; // 父图层 - const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId); + const { layer, parent } = findLayerRecursively( + this.layers.value, + this.layerId + ); // 如果parent 有值 或者 layer 上有parentId 则视为子图层的置顶 if (parent?.id) { // 查找子图层索引 - this.layerIndex = parent?.children?.findIndex((layer) => layer.id === this.layerId); + this.layerIndex = parent?.children?.findIndex( + (layer) => layer.id === this.layerId + ); this.parentLayer = parent; } else { // 查找图层索引 - this.layerIndex = this.layers.value.findIndex((layer) => layer.id === this.layerId); + this.layerIndex = this.layers.value.findIndex( + (layer) => layer.id === this.layerId + ); } this.layer = layer; this.originalIndex = this.layerIndex; // 目标位置 - this.targetIndex = options.direction === "up" ? this.layerIndex - 1 : this.layerIndex + 1; + this.targetIndex = + options.direction === "up" ? this.layerIndex - 1 : this.layerIndex + 1; } async execute() { @@ -749,7 +794,9 @@ export class ToggleLayerVisibilityCommand extends Command { // 更新画布上图层对象的可见性 if (this.canvas) { - const layerObjects = this.canvas.getObjects().filter((obj) => obj.layerId === this.layerId); + const layerObjects = this.canvas + .getObjects() + .filter((obj) => obj.layerId === this.layerId); layerObjects.forEach((obj) => { obj.visible = this.layer.visible; }); @@ -790,7 +837,10 @@ export class ToggleChildLayerVisibilityCommand extends Command { this.layerManager = options.layerManager; // 查找父图层和子图层 - const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId); + const { layer, parent } = findLayerRecursively( + this.layers.value, + this.layerId + ); this.parentLayer = parent; this.childLayer = layer; @@ -807,7 +857,9 @@ export class ToggleChildLayerVisibilityCommand extends Command { // 更新画布上图层对象的可见性 if (this.canvas) { - const layerObjects = this.canvas.getObjects().filter((obj) => obj.layerId === this.layerId); + const layerObjects = this.canvas + .getObjects() + .filter((obj) => obj.layerId === this.layerId); layerObjects.forEach((obj) => { obj.visible = this.childLayer.visible; @@ -866,7 +918,9 @@ export class RenameLayerCommand extends Command { // 更新图层对象上的图层名称 if (this.canvas) { - const layerObjects = this.canvas.getObjects().filter((obj) => obj.layerId === this.layerId); + const layerObjects = this.canvas + .getObjects() + .filter((obj) => obj.layerId === this.layerId); layerObjects.forEach((obj) => { obj.layerName = this.newName; @@ -883,7 +937,9 @@ export class RenameLayerCommand extends Command { // 恢复图层对象上的图层名称 if (this.canvas) { - const layerObjects = this.canvas.getObjects().filter((obj) => obj.layerId === this.layerId); + const layerObjects = this.canvas + .getObjects() + .filter((obj) => obj.layerId === this.layerId); layerObjects.forEach((obj) => { obj.layerName = this.oldName; @@ -918,7 +974,10 @@ export class LayerLockCommand extends Command { // 查找图层(包括子图层) // 查找图层 - const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId); + const { layer, parent } = findLayerRecursively( + this.layers.value, + this.layerId + ); this.layer = layer; this.parentLayer = parent || null; // 父图层 this.oldLocked = this.layer ? this.layer.locked : null; @@ -936,7 +995,11 @@ export class LayerLockCommand extends Command { layer.locked = locked; // 如果是组图层,递归更新所有子图层 - if (layer.type === "group" && layer.children && Array.isArray(layer.children)) { + if ( + layer.type === "group" && + layer.children && + Array.isArray(layer.children) + ) { layer.children.forEach((child) => { this._updateLayerLockState(child, locked); }); @@ -958,7 +1021,11 @@ export class LayerLockCommand extends Command { // 更新画布上对象的可选择状态 await this.layerManager?.updateLayersObjectsInteractivity(); - console.log(`${newLocked ? "锁定" : "解锁"}图层: ${this.layer.name} (ID: ${this.layerId})`); + console.log( + `${newLocked ? "锁定" : "解锁"}图层: ${this.layer.name} (ID: ${ + this.layerId + })` + ); return true; } @@ -970,7 +1037,9 @@ export class LayerLockCommand extends Command { // 更新画布上对象的可选择状态 await this.layerManager?.updateLayersObjectsInteractivity(); - console.log(`恢复图层锁定状态: ${this.layer.name} (锁定: ${this.oldLocked})`); + console.log( + `恢复图层锁定状态: ${this.layer.name} (锁定: ${this.oldLocked})` + ); } } @@ -1018,7 +1087,9 @@ export class SetLayerOpacityCommand extends Command { // 更新画布上对象的不透明度 if (this.canvas) { - const layerObjects = this.canvas.getObjects().filter((obj) => obj.layerId === this.layerId); + const layerObjects = this.canvas + .getObjects() + .filter((obj) => obj.layerId === this.layerId); layerObjects.forEach((obj) => { obj.opacity = this.opacity; @@ -1037,7 +1108,9 @@ export class SetLayerOpacityCommand extends Command { // 更新画布上对象的不透明度 if (this.canvas) { - const layerObjects = this.canvas.getObjects().filter((obj) => obj.layerId === this.layerId); + const layerObjects = this.canvas + .getObjects() + .filter((obj) => obj.layerId === this.layerId); layerObjects.forEach((obj) => { obj.opacity = this.oldOpacity; @@ -1091,7 +1164,9 @@ export class SetLayerBlendModeCommand extends Command { // 更新画布上对象的混合模式 if (this.canvas) { - const layerObjects = this.canvas.getObjects().filter((obj) => obj.layerId === this.layerId); + const layerObjects = this.canvas + .getObjects() + .filter((obj) => obj.layerId === this.layerId); layerObjects.forEach((obj) => { obj.globalCompositeOperation = this.blendMode; @@ -1110,7 +1185,9 @@ export class SetLayerBlendModeCommand extends Command { // 更新画布上对象的混合模式 if (this.canvas) { - const layerObjects = this.canvas.getObjects().filter((obj) => obj.layerId === this.layerId); + const layerObjects = this.canvas + .getObjects() + .filter((obj) => obj.layerId === this.layerId); layerObjects.forEach((obj) => { obj.globalCompositeOperation = this.oldBlendMode; @@ -1150,11 +1227,17 @@ export class MergeLayersCommand extends Command { // 备份原图层 this.originalLayers = [...this.layers.value]; // 新图层ID - this.newLayerId = `merged_layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`; + this.newLayerId = `merged_layer_${Date.now()}_${Math.floor( + Math.random() * 1000 + )}`; } execute() { - if (!this.layerIds || !Array.isArray(this.layerIds) || this.layerIds.length < 2) { + if ( + !this.layerIds || + !Array.isArray(this.layerIds) || + this.layerIds.length < 2 + ) { console.error("合并图层至少需要两个图层"); return null; } @@ -1215,7 +1298,9 @@ export class MergeLayersCommand extends Command { }); // 移除原图层 - this.layers.value = this.layers.value.filter((layer) => !this.layerIds.includes(layer.id)); + this.layers.value = this.layers.value.filter( + (layer) => !this.layerIds.includes(layer.id) + ); // 插入新图层 this.layers.value.splice(topLayerIndex, 0, mergedLayer); @@ -1276,13 +1361,18 @@ export class GroupLayersCommand extends Command { this.originalLayers = [...this.layers.value]; // 新组ID this.groupId = - generateId("group_layer_") || `group_layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`; + generateId("group_layer_") || + `group_layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`; this.originalActiveLayerId = this.activeLayerId.value; // 备份原活动图层ID } async execute() { - if (!this.layerIds || !Array.isArray(this.layerIds) || this.layerIds.length < 2) { + if ( + !this.layerIds || + !Array.isArray(this.layerIds) || + this.layerIds.length < 2 + ) { console.error("组合图层至少需要两个图层"); return null; } @@ -1326,7 +1416,9 @@ export class GroupLayersCommand extends Command { }); // 移除原图层 - this.layers.value = this.layers.value.filter((layer) => !this.layerIds.includes(layer.id)); + this.layers.value = this.layers.value.filter( + (layer) => !this.layerIds.includes(layer.id) + ); // 插入新组图层 this.layers.value.splice(topLayerIndex, 0, groupLayer); @@ -1380,7 +1472,9 @@ export class UngroupLayersCommand extends Command { async execute() { // 查找组图层 - const groupIndex = this.layers.value.findIndex((layer) => layer.id === this.groupId); + const groupIndex = this.layers.value.findIndex( + (layer) => layer.id === this.groupId + ); if (groupIndex === -1) { console.error(`找不到组图层 ${this.groupId}`); @@ -1451,14 +1545,18 @@ export class MergeLayerObjectsCommand extends Command { // 备份原始对象,用于撤销 if (this.activeLayer && Array.isArray(this.activeLayer.fabricObjects)) { this.originalObjects = - this.canvas?.getObjects()?.filter((fItem) => fItem.layerId === this.activeLayer.id) || []; + this.canvas + ?.getObjects() + ?.filter((fItem) => fItem.layerId === this.activeLayer.id) || []; } else { this.originalObjects = []; } // 新合并图像对象 this.mergedImage = null; - this.newImageId = `merged_image_${Date.now()}_${Math.floor(Math.random() * 1000)}`; + this.newImageId = `merged_image_${Date.now()}_${Math.floor( + Math.random() * 1000 + )}`; } async execute() { @@ -1494,7 +1592,10 @@ export class MergeLayerObjectsCommand extends Command { // 异步处理图像合并 try { - const mergedImage = await this._createMergedImageAsync(objectsToMerge, bounds); + const mergedImage = await this._createMergedImageAsync( + objectsToMerge, + bounds + ); this._setupMergedImage(mergedImage, bounds); this._replaceObjects(mergedImage); @@ -1644,7 +1745,9 @@ export class MergeLayerObjectsCommand extends Command { }); tempCanvas.add(clonedObj); - console.log(`添加对象 ${index + 1}/${objects.length}: ${obj.type || "unknown"}`); + console.log( + `添加对象 ${index + 1}/${objects.length}: ${obj.type || "unknown"}` + ); } catch (error) { console.error(`添加对象到临时画布时发生错误:`, error); } @@ -1703,7 +1806,10 @@ export class MergeLayerObjectsCommand extends Command { }); // 如果有新的图像对象,也要移除 - if (this.fabricImage && this.canvas.getObjects().includes(this.fabricImage)) { + if ( + this.fabricImage && + this.canvas.getObjects().includes(this.fabricImage) + ) { this.canvas.remove(this.fabricImage); } @@ -1719,7 +1825,9 @@ export class MergeLayerObjectsCommand extends Command { // 更新缩略图 if (this.canvas.thumbnailManager) { setTimeout(() => { - this.canvas.thumbnailManager.generateLayerThumbnail(this.activeLayer.id); + this.canvas.thumbnailManager.generateLayerThumbnail( + this.activeLayer.id + ); }, 100); } } @@ -1763,7 +1871,9 @@ export class MergeLayerObjectsCommand extends Command { // 更新缩略图 if (this.canvas.thumbnailManager) { setTimeout(() => { - this.canvas.thumbnailManager.generateLayerThumbnail(this.activeLayer.id); + this.canvas.thumbnailManager.generateLayerThumbnail( + this.activeLayer.id + ); }, 100); } } @@ -1851,7 +1961,8 @@ export class LayerObjectsToGroupCommand extends Command { this.existingGroupId = null; this.groupObjectId = null; this.newGroupId = - generateId("group") || `group_${Date.now()}_${Math.floor(Math.random() * 1000)}`; + generateId("group") || + `group_${Date.now()}_${Math.floor(Math.random() * 1000)}`; this.wasGroupCreated = false; // 保存原始位置信息 @@ -1912,7 +2023,10 @@ export class LayerObjectsToGroupCommand extends Command { const newObjectsToAdd = []; if (this.fabricImage) { // 如果是重做,恢复新对象的原始位置 - if (!this.isFirstExecution && this.newObjectPositions.has(this.fabricImage.id)) { + if ( + !this.isFirstExecution && + this.newObjectPositions.has(this.fabricImage.id) + ) { const savedPosition = this.newObjectPositions.get(this.fabricImage.id); this.fabricImage.set({ left: savedPosition.left, @@ -1925,7 +2039,9 @@ export class LayerObjectsToGroupCommand extends Command { flipX: savedPosition.flipX, flipY: savedPosition.flipY, }); - console.log(`🔄 重做时恢复新对象位置: (${savedPosition.left}, ${savedPosition.top})`); + console.log( + `🔄 重做时恢复新对象位置: (${savedPosition.left}, ${savedPosition.top})` + ); } newObjectsToAdd.push(this.fabricImage); @@ -1978,7 +2094,9 @@ export class LayerObjectsToGroupCommand extends Command { if (!this.existingGroupId) { const groupObjects = - this.activeLayer.fabricObjects.find((obj) => obj && obj.type === "group") || null; + this.activeLayer.fabricObjects.find( + (obj) => obj && obj.type === "group" + ) || null; this.existingGroupId = groupObjects?.id || null; } @@ -2078,7 +2196,9 @@ export class LayerObjectsToGroupCommand extends Command { // 更新图层的对象列表 // this.activeLayer.fabricObjects = [groupObject]; - this.activeLayer.fabricObjects = [groupObject.toObject(["id", "layerId", "layerName"])]; + this.activeLayer.fabricObjects = [ + groupObject.toObject(["id", "layerId", "layerName"]), + ]; } /** @@ -2164,7 +2284,10 @@ export class LayerObjectsToGroupCommand extends Command { restorePosition = { ...originalPosition }; } else { // 计算对象在画布中的当前绝对位置 - restorePosition = this._calculateObjectAbsolutePosition(obj, groupObject); + restorePosition = this._calculateObjectAbsolutePosition( + obj, + groupObject + ); } objectsToRestore.push({ @@ -2219,7 +2342,9 @@ export class LayerObjectsToGroupCommand extends Command { restoredObjects.push(obj); console.log( - `✅ 恢复原始对象 ${obj.id || obj.type} 到位置 (${position.left}, ${position.top})` + `✅ 恢复原始对象 ${obj.id || obj.type} 到位置 (${position.left}, ${ + position.top + })` ); } catch (error) { console.error(`恢复对象 ${obj.id || obj.type} 时发生错误:`, error); @@ -2279,7 +2404,10 @@ export class LayerObjectsToGroupCommand extends Command { const objectPoint = new fabric.Point(obj.left || 0, obj.top || 0); // 应用组的变换矩阵计算绝对位置 - const absolutePoint = fabric.util.transformPoint(objectPoint, groupTransform); + const absolutePoint = fabric.util.transformPoint( + objectPoint, + groupTransform + ); // 计算缩放比例 const totalScaleX = (group.scaleX || 1) * (obj.scaleX || 1); @@ -2447,7 +2575,8 @@ export class CreateImageLayerCommand extends Command { // 存储执行过程中的结果 this.newLayerId = - generateId("layer_image_") || `layer_image_${Date.now()}_${Math.floor(Math.random() * 1000)}`; + generateId("layer_image_") || + `layer_image_${Date.now()}_${Math.floor(Math.random() * 1000)}`; this.commands = []; this.executedCommands = []; } @@ -2462,7 +2591,8 @@ export class CreateImageLayerCommand extends Command { this.executedCommands = []; // 生成图层名称 - const fileName = this.layerName || `图片 ${new Date().toLocaleTimeString()}`; + const fileName = + this.layerName || `图片 ${new Date().toLocaleTimeString()}`; this.fabricImage.set({ id: this.imageId, @@ -2533,7 +2663,9 @@ export class CreateImageLayerCommand extends Command { return true; } - console.log(`↩️ 开始撤销创建图片图层操作,共 ${this.executedCommands.length} 个子命令`); + console.log( + `↩️ 开始撤销创建图片图层操作,共 ${this.executedCommands.length} 个子命令` + ); try { // 逆序撤销已执行的命令 @@ -2549,7 +2681,10 @@ export class CreateImageLayerCommand extends Command { } console.log(`✅ 子命令撤销成功: ${command.constructor.name}`); } catch (error) { - console.error(`❌ 子命令撤销失败: ${command.constructor.name}`, error); + console.error( + `❌ 子命令撤销失败: ${command.constructor.name}`, + error + ); } } } @@ -2582,7 +2717,10 @@ export class CreateImageLayerCommand extends Command { } console.log(`✅ 子命令回滚成功: ${command.constructor.name}`); } catch (error) { - console.error(`❌ 子命令回滚失败: ${command.constructor.name}`, error); + console.error( + `❌ 子命令回滚失败: ${command.constructor.name}`, + error + ); } } } @@ -2717,7 +2855,9 @@ export class ReorderChildLayersCommand extends Command { async execute() { // 查找父图层 - const parentLayer = this.layers.value.find((layer) => layer.id === this.parentId); + const parentLayer = this.layers.value.find( + (layer) => layer.id === this.parentId + ); if (!parentLayer) return false; // 获取子图层 @@ -2867,11 +3007,15 @@ export class CutLayerCommand extends Command { // 生成新图层ID和名称 this.newLayerId = - generateId("layer_") || `layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`; + generateId("layer_") || + `layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`; } async execute() { - const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId); + const { layer, parent } = findLayerRecursively( + this.layers.value, + this.layerId + ); const sourceLayer = layer; const parentLayer = parent; @@ -2898,7 +3042,9 @@ export class CutLayerCommand extends Command { blendMode: sourceLayer.blendMode, fabricObjects: [], children: sourceLayer.children ? [...sourceLayer.children] : [], - layerProperties: sourceLayer.layerProperties ? { ...sourceLayer.layerProperties } : {}, + layerProperties: sourceLayer.layerProperties + ? { ...sourceLayer.layerProperties } + : {}, metadata: sourceLayer.metadata ? { ...sourceLayer.metadata } : {}, parentId: parentLayer ? parentLayer.id : undefined, // 保持父图层关系 }); @@ -2908,8 +3054,11 @@ export class CutLayerCommand extends Command { if (parentLayer) { // 处理子图层:在父图层的children数组中插入 - const sourceChildIndex = parentLayer.children.findIndex((child) => child.id === this.layerId); - insertIndex = this.insertIndex !== null ? this.insertIndex : sourceChildIndex + 1; + const sourceChildIndex = parentLayer.children.findIndex( + (child) => child.id === this.layerId + ); + insertIndex = + this.insertIndex !== null ? this.insertIndex : sourceChildIndex + 1; // 插入到父图层的children数组中 parentLayer.children.splice(insertIndex, 0, this.newLayer); @@ -2918,8 +3067,11 @@ export class CutLayerCommand extends Command { this.childInsertIndex = insertIndex; } else { // 处理主图层:在主图层数组中插入 - const sourceIndex = this.layers.value.findIndex((l) => l.id === this.layerId); - insertIndex = this.insertIndex !== null ? this.insertIndex : sourceIndex + 1; + const sourceIndex = this.layers.value.findIndex( + (l) => l.id === this.layerId + ); + insertIndex = + this.insertIndex !== null ? this.insertIndex : sourceIndex + 1; // 插入到主图层数组中 this.layers.value.splice(insertIndex, 0, this.newLayer); @@ -2938,7 +3090,9 @@ export class CutLayerCommand extends Command { // 重新渲染画布 await this.layerManager?.updateLayersObjectsInteractivity(false); - console.log(`已复制图层:${newName} ${this.isChildLayer ? "(子图层)" : "(主图层)"}`); + console.log( + `已复制图层:${newName} ${this.isChildLayer ? "(子图层)" : "(主图层)"}` + ); return this.newLayerId; } @@ -2955,7 +3109,9 @@ export class CutLayerCommand extends Command { } } else { // 撤销主图层:从主图层数组中删除 - const index = this.layers.value.findIndex((l) => l.id === this.newLayerId); + const index = this.layers.value.findIndex( + (l) => l.id === this.newLayerId + ); if (index !== -1) { this.layers.value.splice(index, 1); } @@ -2985,7 +3141,9 @@ export class CutLayerCommand extends Command { fabric.util.enlivenObjects(serializedObjects, (objects) => { objects.forEach((obj) => { // 生成新的对象ID - const newObjId = `obj_${Date.now()}_${Math.floor(Math.random() * 1000)}`; + const newObjId = `obj_${Date.now()}_${Math.floor( + Math.random() * 1000 + )}`; obj.id = newObjId; obj.layerId = this.newLayerId; obj.layerName = this.newLayer.name; @@ -3042,7 +3200,8 @@ export class CreateAdjustmentLayerCommand extends Command { this.newLayer = null; // 生成新图层ID this.newLayerId = - generateId("adj_layer_") || `adj_layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`; + generateId("adj_layer_") || + `adj_layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`; } execute() { @@ -3064,7 +3223,9 @@ export class CreateAdjustmentLayerCommand extends Command { // 计算插入位置 const insertIndex = - this.insertIndex !== null ? this.insertIndex : this._getInsertIndexAboveActiveLayer(); + this.insertIndex !== null + ? this.insertIndex + : this._getInsertIndexAboveActiveLayer(); // 插入新图层 this.layers.value.splice(insertIndex, 0, this.newLayer); @@ -3128,7 +3289,9 @@ export class ApplyLayerStyleCommand extends Command { return; } this.layer = layer; - this.oldStyles = this.layer ? { ...(this.layer.layerProperties?.styles || {}) } : {}; + this.oldStyles = this.layer + ? { ...(this.layer.layerProperties?.styles || {}) } + : {}; } execute() { @@ -3267,7 +3430,9 @@ export class LayerClippingMaskCommand extends Command { if (this.maskLayerId) { // 创建剪贴蒙版 - const maskLayer = this.layers.value.find((l) => l.id === this.maskLayerId); + const maskLayer = this.layers.value.find( + (l) => l.id === this.maskLayerId + ); if (!maskLayer) { console.error(`蒙版图层 ${this.maskLayerId} 不存在`); return false; @@ -3494,14 +3659,19 @@ export class ChangeFixedImageCommand extends Command { this.retryCount = attempt; if (attempt === this.maxRetries) { - throw new Error(`图像加载失败,已重试${this.maxRetries}次: ${error.message}`); + throw new Error( + `图像加载失败,已重试${this.maxRetries}次: ${error.message}` + ); } // 指数退避重试 const delay = Math.pow(2, attempt) * 1000; await new Promise((resolve) => setTimeout(resolve, delay)); - console.warn(`图像加载重试 ${attempt + 1}/${this.maxRetries}:`, error.message); + console.warn( + `图像加载重试 ${attempt + 1}/${this.maxRetries}:`, + error.message + ); } } } @@ -3509,7 +3679,9 @@ export class ChangeFixedImageCommand extends Command { loadImage() { return new Promise((resolve, reject) => { const timeout = setTimeout(() => { - reject(new Error(`图像加载超时 (${this.timeoutMs}ms): ${this.imageUrl}`)); + reject( + new Error(`图像加载超时 (${this.timeoutMs}ms): ${this.imageUrl}`) + ); }, this.timeoutMs); fabric.Image.fromURL( @@ -3547,9 +3719,15 @@ export class ChangeFixedImageCommand extends Command { // 移除旧对象(如果存在) if (this.previousObjectId) { - const { object: oldObject } = findObjectById(this.canvas, this.previousObjectId); + const { object: oldObject } = findObjectById( + this.canvas, + this.previousObjectId + ); if (oldObject) { - const removeSuccess = removeCanvasObjectByObject(this.canvas, oldObject); + const removeSuccess = removeCanvasObjectByObject( + this.canvas, + oldObject + ); if (!removeSuccess) { console.warn("移除旧对象失败,但继续执行"); } @@ -3606,75 +3784,97 @@ export class ChangeFixedImageCommand extends Command { } return new Promise((resolve, reject) => { - fabric.util.enlivenObjects([this.previousImage.objectData], async (objects) => { - try { - const restoredImage = objects[0]; + fabric.util.enlivenObjects( + [this.previousImage.objectData], + async (objects) => { + try { + const restoredImage = objects[0]; - await optimizeCanvasRendering(this.canvas, async () => { - // 移除当前对象 - if (this.newObjectId) { - const { object: currentObject } = findObjectById(this.canvas, this.newObjectId); - if (currentObject) { - removeCanvasObjectByObject(this.canvas, currentObject); + await optimizeCanvasRendering(this.canvas, async () => { + // 移除当前对象 + if (this.newObjectId) { + const { object: currentObject } = findObjectById( + this.canvas, + this.newObjectId + ); + if (currentObject) { + removeCanvasObjectByObject(this.canvas, currentObject); + } } - } - // 恢复之前的变换 - if (this.previousTransform) { - restoredImage.set(this.previousTransform); - } + // 恢复之前的变换 + if (this.previousTransform) { + restoredImage.set(this.previousTransform); + } - // 设置图层属性 - restoredImage.set({ - id: this.previousObjectId, - layerId: this.targetLayer.id, - layerName: this.targetLayer.name, - isBackground: this.targetLayer.isBackground, - isFixed: this.targetLayer.isFixed, + // 设置图层属性 + restoredImage.set({ + id: this.previousObjectId, + layerId: this.targetLayer.id, + layerName: this.targetLayer.name, + isBackground: this.targetLayer.isBackground, + isFixed: this.targetLayer.isFixed, + }); + + // 使用帮助函数在正确的z-index位置恢复对象 + if ( + this.previousZIndex !== undefined && + this.previousZIndex >= 0 + ) { + const insertSuccess = insertObjectAtZIndex( + this.canvas, + restoredImage, + this.previousZIndex, + false + ); + if (insertSuccess) { + console.log(`恢复图像到z-index位置: ${this.previousZIndex}`); + } else { + // 如果插入失败,回退到普通添加 + this.canvas.add(restoredImage); + console.log("z-index恢复失败,恢复图像添加到顶层"); + } + } else { + // 如果没有保存的z-index,添加到顶层 + this.canvas.add(restoredImage); + console.log("恢复图像添加到顶层"); + } + + restoredImage.setCoords(); + + // 更新引用 + this.targetLayer.fabricObject = restoredImage.toObject([ + "id", + "layerId", + "type", + ]); + this.layerManager.updateLayerObject( + this.targetLayer.id, + restoredImage + ); }); - // 使用帮助函数在正确的z-index位置恢复对象 - if (this.previousZIndex !== undefined && this.previousZIndex >= 0) { - const insertSuccess = insertObjectAtZIndex( - this.canvas, - restoredImage, - this.previousZIndex, - false - ); - if (insertSuccess) { - console.log(`恢复图像到z-index位置: ${this.previousZIndex}`); - } else { - // 如果插入失败,回退到普通添加 - this.canvas.add(restoredImage); - console.log("z-index恢复失败,恢复图像添加到顶层"); - } - } else { - // 如果没有保存的z-index,添加到顶层 - this.canvas.add(restoredImage); - console.log("恢复图像添加到顶层"); - } - - restoredImage.setCoords(); - - // 更新引用 - this.targetLayer.fabricObject = restoredImage.toObject(["id", "layerId", "type"]); - this.layerManager.updateLayerObject(this.targetLayer.id, restoredImage); - }); - - resolve(); - } catch (error) { - reject(error); + resolve(); + } catch (error) { + reject(error); + } } - }); + ); }); } async removeCurrentImage() { await optimizeCanvasRendering(this.canvas, async () => { if (this.newObjectId) { - const { object: currentObject } = findObjectById(this.canvas, this.newObjectId); + const { object: currentObject } = findObjectById( + this.canvas, + this.newObjectId + ); if (currentObject) { - const removeSuccess = removeCanvasObjectByObject(this.canvas, currentObject); + const removeSuccess = removeCanvasObjectByObject( + this.canvas, + currentObject + ); if (removeSuccess) { this.targetLayer.fabricObject = null; this.layerManager.updateLayerObject(this.targetLayer.id, null); @@ -3731,7 +3931,10 @@ export class AddImageToLayerCommand extends Command { this.validateInputs(); // 查找目标图层 - const { layer } = findLayerRecursively(this.layerManager?.layers?.value || [], this.layerId); + const { layer } = findLayerRecursively( + this.layerManager?.layers?.value || [], + this.layerId + ); this.targetLayer = layer; @@ -3790,7 +3993,10 @@ export class AddImageToLayerCommand extends Command { this.canvas.remove(this.addedObject); // 从图层管理器中移除 - this.layerManager.removeObjectFromLayer(this.addedObject.id, this.layerId); + this.layerManager.removeObjectFromLayer( + this.addedObject.id, + this.layerId + ); this.isExecuted = false; @@ -3853,14 +4059,19 @@ export class AddImageToLayerCommand extends Command { this.retryCount = attempt; if (attempt === this.maxRetries) { - throw new Error(`图像加载失败,已重试${this.maxRetries}次: ${error.message}`); + throw new Error( + `图像加载失败,已重试${this.maxRetries}次: ${error.message}` + ); } // 指数退避重试 const delay = Math.pow(2, attempt) * 1000; await new Promise((resolve) => setTimeout(resolve, delay)); - console.warn(`图像加载重试 ${attempt + 1}/${this.maxRetries}:`, error.message); + console.warn( + `图像加载重试 ${attempt + 1}/${this.maxRetries}:`, + error.message + ); } } } @@ -3868,7 +4079,9 @@ export class AddImageToLayerCommand extends Command { loadImage() { return new Promise((resolve, reject) => { const timeout = setTimeout(() => { - reject(new Error(`图像加载超时 (${this.timeoutMs}ms): ${this.imageUrl}`)); + reject( + new Error(`图像加载超时 (${this.timeoutMs}ms): ${this.imageUrl}`) + ); }, this.timeoutMs); fabric.Image.fromURL( @@ -3978,7 +4191,9 @@ export class RemoveChildLayerCommand extends Command { this.parentLayer = parent; this.childIndex = - this.parentLayer?.children?.findIndex((child) => child.id === this.layerId) ?? -1; + this.parentLayer?.children?.findIndex( + (child) => child.id === this.layerId + ) ?? -1; this.removedChild = this.parentLayer?.children?.[this.childIndex]; this.isActiveLayer = this.layerId === this.activeLayerId.value; @@ -3993,7 +4208,10 @@ export class RemoveChildLayerCommand extends Command { } // 从画布中移除子图层中的所有对象 - if (this.removedChild.fabricObjects && this.removedChild.fabricObjects.length > 0) { + if ( + this.removedChild.fabricObjects && + this.removedChild.fabricObjects.length > 0 + ) { this.removedChild.fabricObjects.forEach((obj) => { const { object } = findObjectById(this.canvas, obj.id); if (object) { @@ -4012,7 +4230,9 @@ export class RemoveChildLayerCommand extends Command { this.activeLayerId.value = this.parentLayer.children[0].id; } else { this.activeLayerId.value = - this.layers.value.find((layer) => !layer.isBackground || !layer.isFixed)?.[0]?.id || null; + this.layers.value.find( + (layer) => !layer.isBackground || !layer.isFixed + )?.[0]?.id || null; } } @@ -4147,7 +4367,10 @@ export class ChildLayerLockCommand extends Command { this.layerManager = options.layerManager; // 查找父图层和子图层 - const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId); + const { layer, parent } = findLayerRecursively( + this.layers.value, + this.layerId + ); this.parentLayer = parent; this.childLayer = layer; this.oldLocked = this.childLayer ? this.childLayer.locked : null; diff --git a/src/component/Canvas/CanvasEditor/commands/StateCommands.js b/src/component/Canvas/CanvasEditor/commands/StateCommands.js index a7eaa739..70faec77 100644 --- a/src/component/Canvas/CanvasEditor/commands/StateCommands.js +++ b/src/component/Canvas/CanvasEditor/commands/StateCommands.js @@ -25,9 +25,13 @@ export class TransformCommand extends Command { this.layers = options.layers || null; this.lastSelectLayerId = options.lastSelectLayerId || null; // 最后选择的图层ID - const targetObject = findObjectById(this.canvas, this.objectId)?.object || null; + const targetObject = + findObjectById(this.canvas, this.objectId)?.object || null; - const { layer, parent } = findLayerRecursively(this.layers.value, targetObject?.layerId); + const { layer, parent } = findLayerRecursively( + this.layers.value, + targetObject?.layerId + ); this.layer = layer; this.parent = parent; @@ -107,7 +111,7 @@ export class TransformCommand extends Command { if ( this.parent && this.parent?.clippingMask && - this.parent?.children?.length === 1 && + // this.parent?.children?.length === 1 && this.isSginleObject ) { // 计算对象的变换位置 @@ -116,6 +120,20 @@ export class TransformCommand extends Command { this.parent.clippingMask.left -= moveLeft; this.parent.clippingMask.top -= moveTop; + if (this.parent.selectObject) { + // 如果有选区 则选区位置也要更新 + this.parent.selectObject.left = this.parent.clippingMask.left; + this.parent.selectObject.top = this.parent.clippingMask.top; + const { object } = findObjectById( + this.canvas, + this.parent.selectObject?.id + ); + object?.set({ + left: this.parent.clippingMask.left, + top: this.parent.clippingMask.top, + }); + object?.setCoords(); + } // 重新创建遮罩对象 const clippingMaskFabricObject = await restoreFabricObject( diff --git a/src/component/Canvas/CanvasEditor/commands/UpdateGroupMaskPositionCommand.js b/src/component/Canvas/CanvasEditor/commands/UpdateGroupMaskPositionCommand.js index 4ef5be80..cf6114a3 100644 --- a/src/component/Canvas/CanvasEditor/commands/UpdateGroupMaskPositionCommand.js +++ b/src/component/Canvas/CanvasEditor/commands/UpdateGroupMaskPositionCommand.js @@ -1,3 +1,4 @@ +import { findObjectById } from "../utils/helper"; import { findLayerRecursively, isGroupLayer } from "../utils/layerHelper"; import { restoreFabricObject } from "../utils/objectHelper"; import { Command } from "./Command"; @@ -54,8 +55,12 @@ export class UpdateGroupMaskPositionCommand extends Command { }) || this.target?.getBoundingRect?.(true, true); this.originalSelectionPosition = { - left: this.isSginleObject ? this.target.left : this.activeSelection.left || 0, - top: this.isSginleObject ? this.target.top : this.activeSelection.top || 0, + left: this.isSginleObject + ? this.target.left + : this.activeSelection.left || 0, + top: this.isSginleObject + ? this.target.top + : this.activeSelection.top || 0, }; console.log( @@ -74,6 +79,21 @@ export class UpdateGroupMaskPositionCommand extends Command { this.layer.clippingMask.left = newLeft; this.layer.clippingMask.top = newTop; + if (this.layer.selectObject) { + // 如果有选区 则选区位置也要更新 + this.layer.selectObject.left = this.layer.clippingMask.left; + this.layer.selectObject.top = this.layer.clippingMask.top; + const { object } = findObjectById( + this.canvas, + this.layer.selectObject?.id + ); + object?.set({ + left: this.layer.clippingMask.left, + top: this.layer.clippingMask.top, + }); + object?.setCoords(); + } + this.newMaskPosition = { left: newLeft, top: newTop, @@ -112,6 +132,21 @@ export class UpdateGroupMaskPositionCommand extends Command { layer.clippingMask.left = this.originalMaskPosition.left; layer.clippingMask.top = this.originalMaskPosition.top; + if (this.layer.selectObject) { + // 如果有选区 则选区位置也要更新 + this.layer.selectObject.left = this.originalMaskPosition.left; + this.layer.selectObject.top = this.originalMaskPosition.top; + const { object } = findObjectById( + this.canvas, + this.layer.selectObject?.id + ); + object?.set({ + left: this.originalMaskPosition.left, + top: this.originalMaskPosition.top, + }); + object?.setCoords(); + } + // 更新所有使用此遮罩的子图层对象 await this._updateChildObjectsClipPath(layer, true); // await this.layerManager.updateLayersObjectsInteractivity(); @@ -147,6 +182,21 @@ export class UpdateGroupMaskPositionCommand extends Command { layer.clippingMask.left = newLeft; layer.clippingMask.top = newTop; + if (this.layer.selectObject) { + // 如果有选区 则选区位置也要更新 + this.layer.selectObject.left = this.layer.clippingMask.left; + this.layer.selectObject.top = this.layer.clippingMask.top; + const { object } = findObjectById( + this.canvas, + this.layer.selectObject?.id + ); + object?.set({ + left: this.layer.clippingMask.left, + top: this.layer.clippingMask.top, + }); + object?.setCoords(); + } + // 更新所有使用此遮罩的子图层对象(不需要等待) this._updateChildObjectsClipPath(layer); @@ -173,7 +223,10 @@ export class UpdateGroupMaskPositionCommand extends Command { try { // 重新创建遮罩对象 - const clippingMaskFabricObject = await restoreFabricObject(layer.clippingMask, this.canvas); + const clippingMaskFabricObject = await restoreFabricObject( + layer.clippingMask, + this.canvas + ); if (!clippingMaskFabricObject) { console.warn("无法恢复遮罩对象"); @@ -192,7 +245,9 @@ export class UpdateGroupMaskPositionCommand extends Command { layer.children.forEach((childLayer) => { // 更新 fabricObjects 中的对象 childLayer.fabricObjects?.forEach((obj) => { - const fabricObject = this.canvas.getObjects().find((o) => o.id === obj.id); + const fabricObject = this.canvas + .getObjects() + .find((o) => o.id === obj.id); if (fabricObject) { fabricObject.clipPath = clippingMaskFabricObject; fabricObject.dirty = true; @@ -215,7 +270,9 @@ export class UpdateGroupMaskPositionCommand extends Command { if (layer?.fill) { // 更新组图层的填充颜色 - const fabricObject = this.canvas.getObjects().find((o) => o.id === layer?.fill.id); + const fabricObject = this.canvas + .getObjects() + .find((o) => o.id === layer?.fill.id); if (fabricObject) { fabricObject.clipPath = clippingMaskFabricObject; fabricObject.dirty = true; diff --git a/src/component/Canvas/CanvasEditor/managers/CanvasManager.js b/src/component/Canvas/CanvasEditor/managers/CanvasManager.js index 14d2b2b9..ed8327a3 100644 --- a/src/component/Canvas/CanvasEditor/managers/CanvasManager.js +++ b/src/component/Canvas/CanvasEditor/managers/CanvasManager.js @@ -1,5 +1,7 @@ import { fabric } from "fabric-with-all"; -import initAligningGuidelines, { initCenteringGuidelines } from "../utils/helperLine"; +import initAligningGuidelines, { + initCenteringGuidelines, +} from "../utils/helperLine"; import { ThumbnailManager } from "./ThumbnailManager"; import { ExportManager } from "./ExportManager"; import { @@ -14,7 +16,12 @@ import { CanvasEventManager } from "./events/CanvasEventManager"; import CanvasConfig from "../config/canvasConfig"; import { RedGreenModeManager } from "./RedGreenModeManager"; import { EraserStateManager } from "./EraserStateManager"; -import { deepClone, generateId, optimizeCanvasRendering } from "../utils/helper"; +import { + deepClone, + findObjectById, + generateId, + optimizeCanvasRendering, +} from "../utils/helper"; import { ChangeFixedImageCommand } from "../commands/ObjectLayerCommands"; import { isFunction } from "lodash-es"; import { @@ -150,7 +157,10 @@ export class CanvasManager { return false; }; - this.eraserStateManager = new EraserStateManager(this.canvas, this.layerManager); + this.eraserStateManager = new EraserStateManager( + this.canvas, + this.layerManager + ); // 监听擦除开始事件 this.canvas.on("erasing:start", () => { @@ -171,12 +181,17 @@ export class CanvasManager { } // 更新交互性 - command && (await this.layerManager?.updateLayersObjectsInteractivity?.()); + command && + (await this.layerManager?.updateLayersObjectsInteractivity?.()); - this.thumbnailManager?.generateLayerThumbnail(this.layerManager?.activeLayerId?.value); + this.thumbnailManager?.generateLayerThumbnail( + this.layerManager?.activeLayerId?.value + ); // 固定图层 的擦除也需要重新生成缩略图 要判断 当前固定图层是否锁定 - const fixedLayer = this.layers?.value?.find((layer) => layer.isFixed && !layer.locked); + const fixedLayer = this.layers?.value?.find( + (layer) => layer.isFixed && !layer.locked + ); // 如果有固定图层且未锁定,则生成缩略图 fixedLayer && this.isFixedErasable && @@ -366,7 +381,9 @@ export class CanvasManager { // 设置固定图层的可擦除状态 layer.locked = flag; // 更新画布对象的erasable属性 - const fabricObject = this.canvas.getObjects().find((obj) => obj.id === layer.id); + const fabricObject = this.canvas + .getObjects() + .find((obj) => obj.id === layer.id); if (fabricObject) { fabricObject.erasable = flag; fabricObject.set("erasable", flag); @@ -478,7 +495,9 @@ export class CanvasManager { const deltaY = backgroundObject.top - backgroundOldTop; // 将相同的偏移量应用到所有其他对象上 - const otherObjects = visibleObjects.filter((obj) => obj !== backgroundObject); + const otherObjects = visibleObjects.filter( + (obj) => obj !== backgroundObject + ); otherObjects.forEach((obj) => { obj.set({ @@ -496,12 +515,29 @@ export class CanvasManager { // 如果图层有遮罩,更新遮罩位置 layer.clippingMask.left += deltaX; layer.clippingMask.top += deltaY; + + if (layer.selectObject) { + // 如果有选区 则选区位置也要更新 + layer.selectObject.left = layer.clippingMask.left; + layer.selectObject.top = layer.clippingMask.top; + const { object } = findObjectById( + this.canvas, + layer.selectObject?.id + ); + object?.set({ + left: layer.clippingMask.left, + top: layer.clippingMask.top, + }); + object?.setCoords(); + } } }); if (isMaskLayer) { - requestAnimationFrame(() => { - this.layerManager?.updateLayersObjectsInteractivity?.(); + setTimeout(() => { + this.layerManager?.updateLayersObjectsInteractivity?.(false, { + isMoveing: true, + }); }); } @@ -598,7 +634,8 @@ export class CanvasManager { if (!backgroundLayerObject) return; const bgWidth = backgroundLayerObject.width * backgroundLayerObject.scaleX; - const bgHeight = backgroundLayerObject.height * backgroundLayerObject.scaleY; + const bgHeight = + backgroundLayerObject.height * backgroundLayerObject.scaleY; const left = backgroundLayerObject.left; const top = backgroundLayerObject.top; @@ -659,7 +696,9 @@ export class CanvasManager { return obj.isBackground || obj.id === backgroundLayerId; }); if (!backgroundLayerByBgLayer) { - console.warn("CanvasManager.js = >getBackgroundLayer 方法没有找到背景层"); + console.warn( + "CanvasManager.js = >getBackgroundLayer 方法没有找到背景层" + ); } return backgroundLayerByBgLayer; @@ -669,7 +708,8 @@ export class CanvasManager { * @param {Object} backgroundLayerObject 背景层对象 */ updateMaskPosition(backgroundLayerObject) { - if (!backgroundLayerObject || !this.maskLayer || !this.canvas.clipPath) return; + if (!backgroundLayerObject || !this.maskLayer || !this.canvas.clipPath) + return; const left = backgroundLayerObject.left; const top = backgroundLayerObject.top; @@ -734,7 +774,8 @@ export class CanvasManager { ...options, }); - command.undoable = options.undoable !== undefined ? options.undoable : false; // 默认不可撤销 undoable = true 为可撤销 + command.undoable = + options.undoable !== undefined ? options.undoable : false; // 默认不可撤销 undoable = true 为可撤销 const result = (await command?.execute?.()) || { success: false, @@ -784,7 +825,9 @@ export class CanvasManager { ...options, // 如果没有明确指定,则根据当前模式自动设置 restoreOpacityInRedGreen: - options.restoreOpacityInRedGreen !== undefined ? options.restoreOpacityInRedGreen : true, // 默认在红绿图模式下恢复透明度 + options.restoreOpacityInRedGreen !== undefined + ? options.restoreOpacityInRedGreen + : true, // 默认在红绿图模式下恢复透明度 }; // 如果在红绿图模式下且没有指定具体的图层,自动包含所有普通图层 @@ -798,7 +841,9 @@ export class CanvasManager { // 获取所有非背景、非固定的普通图层ID const normalLayerIds = this.layers?.value - ?.filter((layer) => !layer.isBackground && !layer.isFixed && layer.visible) + ?.filter( + (layer) => !layer.isBackground && !layer.isFixed && layer.visible + ) ?.map((layer) => layer.id) || []; if (normalLayerIds.length > 0) { @@ -911,7 +956,9 @@ export class CanvasManager { this.canvas.discardActiveObject(); this.canvas.renderAll(); - const simplifyLayersData = simplifyLayers(JSON.parse(JSON.stringify(this.layers.value))); + const simplifyLayersData = simplifyLayers( + JSON.parse(JSON.stringify(this.layers.value)) + ); console.log("获取画布JSON数据...", simplifyLayersData); return JSON.stringify({ canvas: this.canvas.toJSON([ @@ -927,6 +974,7 @@ export class CanvasManager { "eraser", "eraserable", "erasable", + "customType", ]), layers: simplifyLayersData, // 简化图层数据 // layers: JSON.stringify(JSON.parse(JSON.stringify(this.layers.value))), // 全数据 @@ -1033,7 +1081,8 @@ export class CanvasManager { // 使用LayerSort工具重新排列画布对象(如果可用) await this?.layerManager?.layerSort?.rearrangeObjects(); - this.layerManager.activeLayerId.value = this.layers.value[0].children?.length + this.layerManager.activeLayerId.value = this.layers.value[0] + .children?.length ? this.layers.value[0].children[0].id : this.layers.value[0]?.id || parsedJson?.activeLayerId || null; @@ -1046,7 +1095,9 @@ export class CanvasManager { await calllBack?.(); // 确保所有对象的交互性正确设置 - await this.layerManager?.updateLayersObjectsInteractivity?.(false); + await this.layerManager?.updateLayersObjectsInteractivity?.( + false + ); console.log(this.layerManager.layers.value); // 更新所有缩略图 @@ -1203,7 +1254,9 @@ export class CanvasManager { if (!this.layers || !this.layers.value) return []; // 查找所有非背景、非固定的普通图层 - const normalLayers = this.layers.value.filter((layer) => !layer.isBackground && !layer.isFixed); + const normalLayers = this.layers.value.filter( + (layer) => !layer.isBackground && !layer.isFixed + ); // 收集所有普通图层中的对象 const objects = []; @@ -1240,9 +1293,15 @@ export class CanvasManager { // 比较尺寸(允许5%的误差) const sizeMatch = - Math.abs(obj.width * obj.scaleX - fixedLayerObject.width * fixedLayerObject.scaleX) < + Math.abs( + obj.width * obj.scaleX - + fixedLayerObject.width * fixedLayerObject.scaleX + ) < fixedLayerObject.width * fixedLayerObject.scaleX * 0.05 && - Math.abs(obj.height * obj.scaleY - fixedLayerObject.height * fixedLayerObject.scaleY) < + Math.abs( + obj.height * obj.scaleY - + fixedLayerObject.height * fixedLayerObject.scaleY + ) < fixedLayerObject.height * fixedLayerObject.scaleY * 0.05; // 比较位置(允许一定的偏差) diff --git a/src/component/Canvas/CanvasEditor/managers/LayerManager.js b/src/component/Canvas/CanvasEditor/managers/LayerManager.js index 9f085643..71f948d0 100644 --- a/src/component/Canvas/CanvasEditor/managers/LayerManager.js +++ b/src/component/Canvas/CanvasEditor/managers/LayerManager.js @@ -64,6 +64,7 @@ import { isBoolean, template } from "lodash-es"; import { findObjectById, generateId, + insertObjectAtZIndex, optimizeCanvasRendering, } from "../utils/helper"; import { message } from "ant-design-vue"; @@ -189,19 +190,40 @@ export class LayerManager { * 根据当前编辑模式和图层状态设置对象的交互属性 * @private */ - async updateLayersObjectsInteractivity(isUseOptimize = true) { + async updateLayersObjectsInteractivity( + isUseOptimize = true, + { isMoveing = false } = {} + ) { if (!this.canvas) return; if (isUseOptimize) { // 优化渲染 - 统一批处理 支持异步回调 await optimizeCanvasRendering(this.canvas, async () => { // 应用图层交互规则 - await this._applyInteractionRules(); + await this._applyInteractionRules({ isMoveing }); }); } else { // 直接应用图层交互规则 - await this._applyInteractionRules(); + await this._applyInteractionRules({ isMoveing }); } + // customType: "selectionObject", + // 清空没有关联的选区图层 + const selectionObjects = this.canvas + .getObjects() + ?.filter((obj) => obj.customType == "selectionObject"); + + selectionObjects.forEach((obj) => { + const isObjExtis = this.layers?.value.find( + (layer) => layer?.selectObject?.id == obj.id + ); + if (!isObjExtis) { + // 不存在则移除对象 + this?.canvas?.remove(obj); + } + }); + + selectionObjects && this.canvas.renderAll(); + // // 性能优化:使用requestAnimationFrame // requestAnimationFrame(() => { // // 暂停渲染以提高性能 @@ -304,7 +326,7 @@ export class LayerManager { } // 私有方法:应用交互规则 - async _applyInteractionRules() { + async _applyInteractionRules({ isMoveing }) { console.log("updateLayersObjectsInteractivity ===>", this.editorMode); const objects = this.canvas.getObjects(); const editorMode = this.editorMode || CanvasConfig.defaultTool; @@ -414,6 +436,68 @@ export class LayerManager { // } // } } + + // 设置画布选区内容 + if (layer.selectObject && !isMoveing) { + // 如果有选区对象 + let insetIndex = objects.findIndex( + (o) => o.id === layer.selectObject.id + ); + let isOldSelectObject = insetIndex >= 0; // 是否存在旧的选区对象 + const newSelectObject = await restoreFabricObject( + layer.selectObject, + this.canvas + ); + + if (insetIndex < 0) { + // 如果没有找到选区对象,则添加到画布 先查找子图层元素下标 如果没有则要向前查找有元素的下标 否则直接添加,因为选区要遮罩在选区图层的最上面 + layer.children?.forEach((childLayer) => { + const childObj = this.canvas + .getObjects() + .find((o) => o.layerId === childLayer.id); + if (childObj) { + insetIndex = objects.indexOf(childObj); + } + }); + } + + // 向前查找,找不到则添加到最后 + if (insetIndex < 0) { + // 如果没有找到选区对象,则添加到画布 + const layerIndex = this.layers.value.findIndex( + (ll) => ll.id === layer.id + ); + + for (let i = layerIndex - 1; i >= 0; i--) { + const l = this.layers.value[i]; + if (l.fabricObjects?.length > 0) { + insetIndex = objects.findIndex( + (o) => o.id === l.fabricObjects?.[0].id + ); + if (insetIndex >= 0) break; // 找到第一个有对象的图层 + } + } + insetIndex = insetIndex < 0 ? objects.length : insetIndex; + } + + newSelectObject.set({ + id: layer.selectObject.id, + customType: layer.selectObject.customType, + selectable: false, + evented: false, + erasable: false, + }); + + // 有则更新对象 + // 没有则反序列创建新的选区对象 + insertObjectAtZIndex( + this.canvas, + newSelectObject, + insetIndex, + false, + isOldSelectObject + ); + } }); } @@ -871,6 +955,7 @@ export class LayerManager { layers: this.layers, layerId: layerId, activeLayerId: this.activeLayerId, + layerManager: this, }); // 执行命令 diff --git a/src/component/Canvas/CanvasEditor/utils/layerUtils.js b/src/component/Canvas/CanvasEditor/utils/layerUtils.js index 1a84e616..00bc1e68 100644 --- a/src/component/Canvas/CanvasEditor/utils/layerUtils.js +++ b/src/component/Canvas/CanvasEditor/utils/layerUtils.js @@ -15,13 +15,18 @@ export function buildLayerAssociations(layer, canvasObjects) { // 处理单个fabricObject关联 if (layer.fabricObject) { // 如果图层已经有关联的fabricObject,确保它的layerId和layerName正确 - layer.fabricObject = canvasObjects.find((obj) => obj.id === layer.fabricObject.id) || null; + layer.fabricObject = + canvasObjects.find((obj) => obj.id === layer.fabricObject.id) || null; } if (layer.clippingMask) { // clippingMask 可能是一个fabricObject或组 也可能是一个简单对象 - const clippingMaskObj = canvasObjects.find((obj) => obj.id === layer.clippingMask.id); - layer.clippingMask = clippingMaskObj ? clippingMaskObj?.toObject?.(["id"]) : layer.clippingMask; + const clippingMaskObj = canvasObjects.find( + (obj) => obj.id === layer.clippingMask.id + ); + layer.clippingMask = clippingMaskObj + ? clippingMaskObj?.toObject?.(["id"]) + : layer.clippingMask; } // 处理多个fabricObjects关联 @@ -167,7 +172,10 @@ export function simplifyLayers(layers) { opacity: layer.opacity, isBackground: layer.isBackground || false, isFixed: layer.isFixed || false, - clippingMask: layer.clippingMask?.toObject?.(["id", "layerId"]) || layer.clippingMask || null, // 可能是一个fabricObject或组 也可能是一个简单对象 + clippingMask: + layer.clippingMask?.toObject?.(["id", "layerId"]) || + layer.clippingMask || + null, // 可能是一个fabricObject或组 也可能是一个简单对象 // ? { // id: layer.clippingMask.id, // type: layer.clippingMask.type, @@ -192,9 +200,13 @@ export function simplifyLayers(layers) { ) .filter((obj) => obj !== null) : [], - children: layer.children && isArray(layer.children) ? simplifyLayers(layer.children) : [], + children: + layer.children && isArray(layer.children) + ? simplifyLayers(layer.children) + : [], fill: layer?.fill || null, fillColor: layer.fillColor, + selectObject: layer.selectObject, }; return simplifiedLayer; @@ -231,7 +243,9 @@ export function restoreLayers(simplifiedLayers, canvasObjects) { if (layer.clippingMask) { // clippingMask 可能是一个fabricObject或组 也可能是一个简单对象 - const clippingMaskObj = canvasObjects.find((obj) => obj.id === layer.clippingMask.id); + const clippingMaskObj = canvasObjects.find( + (obj) => obj.id === layer.clippingMask.id + ); restoredLayer.clippingMask = clippingMaskObj ? clippingMaskObj?.toObject?.(["id"]) : layer.clippingMask; @@ -239,11 +253,17 @@ export function restoreLayers(simplifiedLayers, canvasObjects) { // 恢复单个fabricObject关联 if (layer.fabricObject?.id) { - const fabricObj = canvasObjects.find((obj) => obj.id === layer.fabricObject.id); + const fabricObj = canvasObjects.find( + (obj) => obj.id === layer.fabricObject.id + ); if (fabricObj) { fabricObj.layerId = layer.id; fabricObj.layerName = layer.name; - restoredLayer.fabricObject = fabricObj.toObject(["id", "layerId", "type"]); + restoredLayer.fabricObject = fabricObj.toObject([ + "id", + "layerId", + "type", + ]); } else { restoredLayer.fabricObject = null; } @@ -253,7 +273,9 @@ export function restoreLayers(simplifiedLayers, canvasObjects) { if (layer.fabricObjects && isArray(layer.fabricObjects)) { restoredLayer.fabricObjects = layer.fabricObjects .map((fabricRef) => { - const fabricObj = canvasObjects.find((obj) => obj.id === fabricRef.id); + const fabricObj = canvasObjects.find( + (obj) => obj.id === fabricRef.id + ); if (fabricObj) { fabricObj.layerId = layer.id; fabricObj.layerName = layer.name;