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 { fabric } from "fabric-with-all"; import { generateId } from "../utils/helper.js"; import { ToolCommand } from "./ToolCommands.js"; import i18n from "@/lang/index.ts"; const { t } = i18n.global; /** * 套索抠图命令 * 实现将选区内容到新图层遮罩 */ export class LassoCutoutCommand extends CompositeCommand { constructor(options = {}) { super([], { name: "套索抠图", description: "将选区抠图到新图层", }); this.canvas = options.canvas; this.layerManager = options.layerManager; this.selectionManager = options.selectionManager; this.toolManager = options.toolManager; this.sourceLayerId = options.sourceLayerId; this.newLayerName = options.newLayerName || "抠图"; this.newLayerId = null; this.cutoutImageUrl = null; this.fabricImage = null; this.executedCommands = []; // 高清截图选项 this.highResolutionEnabled = options.highResolutionEnabled !== false; // 默认启用 this.baseResolutionScale = options.baseResolutionScale || 2; // 基础分辨率倍数 this.groupId = options.groupId || generateId("lasso-group-"); this.groupName = options.groupName || t(`Canvas.ConstituencyGroup`); this.clippingMaskId = generateId("clipping-mask-"); this.groupLayer = null; // 新增:保存组图层的引用 this.originalLayersLength = 0; // 新增:保存原始图层数量 // 在初始化时克隆保存选区对象,避免撤销后重做时获取不到选区对象 this._clonedSelectionObject = null; this._initializeClonedSelection(); // 新增:保存原图层信息用于撤销恢复 this.originalLayer = null; // 保存原图层的完整信息 this.originalLayerIndex = -1; // 保存原图层在layers数组中的索引 this.originalFabricObjects = []; // 保存原图层的所有fabric对象(序列化) this.originalCanvasObjects = []; // 保存从画布中获取的真实fabric对象 this._initializeOriginalLayerInfo(); this.oldActiveLayerId = this.layerManager.activeLayerId.value; // 保存旧的活动图层ID } /** * 初始化克隆的选区对象 * @private */ async _initializeClonedSelection() { if (this.selectionManager) { const selectionObject = this.selectionManager.getSelectionObject(); if (selectionObject) { try { this._clonedSelectionObject = await this._cloneObject( selectionObject ); console.log("套索抠图:选区对象已克隆保存"); } catch (error) { console.error("套索抠图:克隆选区对象失败:", error); // 备用方案:序列化保存 this.serializedSelectionObject = selectionObject.toObject([ "id", "layerId", "layerName", "parentId", ]); } } } } /** * 初始化原图层信息 * @private */ _initializeOriginalLayerInfo() { if (this.layerManager) { const activeLayer = this.layerManager.getActiveLayer(); if (activeLayer) { // 保存原图层的完整信息 this.originalLayer = JSON.parse(JSON.stringify(activeLayer)); // 深拷贝 // 保存原图层在layers数组中的索引 this.originalLayerIndex = this.layerManager.layers.value.findIndex( (layer) => layer.id === activeLayer.id ); // 获取并序列化原图层的所有fabric对象 const sourceObjects = this._getLayerObjects(activeLayer); this.originalCanvasObjects = sourceObjects; // 保存真实对象引用 this.originalFabricObjects = sourceObjects.map((obj) => obj.toObject([ "id", "layerId", "layerName", "parentId", "type", "custom", ]) ); console.log( `套索抠图:已保存原图层信息,图层名: ${activeLayer.name},对象数量: ${sourceObjects.length}` ); } } } async execute() { if (!this.canvas || !this.layerManager || !this.selectionManager) { console.error("无法执行套索抠图:参数无效"); return false; } try { this.executedCommands = []; // 保存原始图层数量,用于撤销时的验证 this.originalLayersLength = this.layerManager.layers.value.length; // 获取选区:优先使用克隆的选区对象 const selectionObject = await this._getSelectionObject(); if (!selectionObject) { console.error("无法执行套索抠图:当前没有选区"); return false; } // 确定源图层 const sourceLayer = this.layerManager.getActiveLayer(); if (!sourceLayer) { console.error("无法执行套索抠图:源图层无效"); return false; } // 获取源图层的所有对象(包括子图层) // const sourceObjects = this._getLayerObjects(sourceLayer); // if (sourceObjects.length === 0) { // console.error("无法执行套索抠图:源图层没有可见对象"); // return false; // } const clippingMask = fabric.util.object.clone(selectionObject); clippingMask.set({ id: this.clippingMaskId, selectable: false, evented: false, hasControls: false, // layerId: this.groupId, visible: true, }); // 获取选区边界信息用于后续定位 // const selectionBounds = selectionObject.getBoundingRect(true, true); // 使用createRasterizedImage执行抠图操作 // this.cutoutImageUrl = await this._performCutoutWithRasterized( // sourceObjects, // selectionObject, // selectionBounds // ); // if (!this.cutoutImageUrl) { // console.error("抠图失败"); // return false; // } // this.fabricImage = await this._performCutoutWithRasterized( // sourceObjects, // selectionObject, // selectionBounds // ); // // 创建fabric图像对象,传递选区边界信息 // this.fabricImage = await this._createFabricImage( // this.cutoutImageUrl, // selectionBounds // ); // if (!this.fabricImage) { // console.error("创建图像对象失败"); // return false; // } // 1. 创建图像图层命令 // const createImageLayerCmd = new CreateImageLayerCommand({ // layerManager: this.layerManager, // fabricImage: this.fabricImage, // toolManager: this.toolManager, // layerName: this.newLayerName, // }); // // 执行创建图像图层命令 // const result = await createImageLayerCmd.execute(); // this.newLayerId = createImageLayerCmd.newLayerId; // this.executedCommands.push(createImageLayerCmd); // 3. 清除选区命令 const clearSelectionCmd = new ClearSelectionCommand({ canvas: this.canvas, selectionManager: this.selectionManager, }); // 执行清除选区命令 await clearSelectionCmd.execute(); this.executedCommands.push(clearSelectionCmd); const topLayerIndex = this.layerManager.layers.value.findIndex( (layer) => layer.id === this.originalLayer.id ); // const selectLayer = this.layerManager.layers.value[topLayerIndex]; // 创建新的组图层 this.groupLayer = createLayer({ id: this.groupId, name: this.groupName || t(`Canvas.ConstituencyGroup`), type: LayerType.GROUP, visible: true, locked: false, opacity: 1.0, fabricObjects: [], children: [], }); // this.fabricImage.set({ // selectable: true, // evented: true, // }); const selectLayer = createLayer({ name: t(`Canvas.ConstituencyEmptyLayer`), type: LayerType.EMPTY, visible: true, locked: false, opacity: 1.0, fabricObjects: [], children: [], }); selectLayer.parentId = this.groupId; // 设置新图层的parentId为组图层ID // selectLayer.fabricObjects = [ // this.fabricImage.toObject("id", "layerId", "layerName", "parentId"), // ]; // 2. 删除原图层命令 // const removeOriginalLayerCmd = new RemoveLayerCommand({ // canvas: this.canvas, // layers: this.layerManager.layers, // layerId: this.originalLayer.id, // activeLayerId: this.layerManager.activeLayerId, // layerManager: this.layerManager, // }); // // 执行删除原图层命令 // await removeOriginalLayerCmd.execute(); // this.executedCommands.push(removeOriginalLayerCmd); 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); this.layerManager.activeLayerId.value = selectLayer.id; // 设置新组图层为活动图层 // 切换工具到选择模式 // 3. 切换工具到选择模式命令 if (this.toolManager) { const toolCmd = new ToolCommand({ toolManager: this.toolManager, tool: OperationType.SELECT, previousTool: this.toolManager.getCurrentTool(), }); // 执行工具切换命令 await toolCmd.execute(); this.commands.push(toolCmd); this.executedCommands.push(toolCmd); } this.canvas.discardActiveObject(); // this.canvas.setActiveObject(this.fabricImage); await this.layerManager.updateLayersObjectsInteractivity(true); console.log(`套索抠图完成,新图层ID: ${this.newLayerId}`); return { newLayerId: this.newLayerId, cutoutImageUrl: this.cutoutImageUrl, guroupId: this.groupId, groupName: this.groupName, }; } catch (error) { console.error("套索抠图过程中出错:", error); // 如果已经创建了新图层,需要进行清理 if (this.newLayerId) { try { await this.layerManager.removeLayer(this.newLayerId); } catch (cleanupError) { console.warn("清理新图层失败:", cleanupError); } } // 清理组图层(如果已创建) if (this.groupLayer && this.groupId) { try { const groupIndex = this.layerManager.layers.value.findIndex( (layer) => layer.id === this.groupId ); if (groupIndex !== -1) { this.layerManager.layers.value.splice(groupIndex, 1); console.log(`清理了异常创建的组图层: ${this.groupId}`); } this.groupLayer = null; } catch (cleanupError) { console.warn("清理组图层失败:", cleanupError); } } // 尝试回滚已执行的命令 if (this.executedCommands.length > 0) { try { for (let i = this.executedCommands.length - 1; i >= 0; i--) { const command = this.executedCommands[i]; if (command && typeof command.undo === "function") { await command.undo(); } } this.executedCommands = []; } catch (rollbackError) { console.warn("回滚已执行命令失败:", rollbackError); } } throw error; } } async undo() { try { console.log(`↩️ 开始撤销套索抠图操作`); // 1. 首先移除组图层(如果存在) if (this.groupId) { const groupIndex = this.layerManager.layers.value.findIndex( (layer) => layer.id === this.groupId ); if (groupIndex !== -1) { console.log(`↩️ 移除组图层: ${this.groupId}`); // 从图层列表中移除组图层 this.layerManager.layers.value.splice(groupIndex, 1); } } this.layerManager.activeLayerId.value = this.oldActiveLayerId; // 恢复旧的活动图层ID if (this.fabricImage) { console.log(`↩️ 移除抠图图像: ${this.fabricImage.id}`); // 从画布中移除抠图图像 this.canvas.remove(this.fabricImage); } // 2. 逆序撤销所有已执行的子命令 // RemoveLayerCommand的undo会自动恢复原图层,所以不需要再调用_restoreOriginalLayer for (let i = this.executedCommands.length - 1; i >= 0; i--) { const command = this.executedCommands[i]; if (command && typeof command.undo === "function") { try { console.log(`↩️ 撤销子命令: ${command.constructor.name}`); await command.undo(); console.log(`✅ 子命令撤销成功: ${command.constructor.name}`); } catch (error) { console.error( `❌ 子命令撤销失败: ${command.constructor.name}`, error ); // 子命令撤销失败不中断整个撤销过程 } } } // 注意:不需要调用_restoreOriginalLayer,因为RemoveLayerCommand的undo已经恢复了原图层 // 但是需要确保画布状态正确,所以手动触发一次渲染 if (this.canvas) { this.canvas.renderAll(); } // 3. 清理状态 this.executedCommands = []; this.newLayerId = null; this.cutoutImageUrl = null; this.fabricImage = null; this.groupLayer = null; // 清理组图层引用 // 注意:不重置groupId,因为重做时可能需要使用相同的ID // 4. 更新画布和图层交互性 await this.layerManager.updateLayersObjectsInteractivity(true); console.log(`✅ 套索抠图撤销完成`); return true; } catch (error) { console.error("❌ 撤销套索抠图失败:", error); return false; } } /** * 获取命令信息 * @returns {Object} 命令详细信息 */ getInfo() { return { name: this.name, description: this.description, newLayerId: this.newLayerId, newLayerName: this.newLayerName, groupId: this.groupId, groupName: this.groupName, executedCommandsCount: this.executedCommands.length, hasGroupLayer: !!this.groupLayer, sourceLayerId: this.sourceLayerId, highResolutionEnabled: this.highResolutionEnabled, baseResolutionScale: this.baseResolutionScale, hasSerializedSelection: !!this.serializedSelectionObject, selectionType: this.serializedSelectionObject?.type || null, // 新增:原图层信息 hasOriginalLayer: !!this.originalLayer, originalLayerName: this.originalLayer?.name || null, originalLayerId: this.originalLayer?.id || null, originalLayerIndex: this.originalLayerIndex, originalFabricObjectsCount: this.originalFabricObjects.length, originalCanvasObjectsCount: this.originalCanvasObjects.length, subCommands: this.executedCommands.map((cmd) => ({ name: cmd.constructor.name, info: cmd.getInfo ? cmd.getInfo() : {}, })), }; } /** * 获取图层的所有对象(包括子图层,从画布中查找真实对象) * @param {Object} layer 图层对象 * @returns {Array} 真实的fabric对象数组 * @private */ _getLayerObjects(layer) { const objects = []; const canvasObjects = this.canvas.getObjects(); // 递归获取图层及其子图层的所有对象 const collectLayerObjects = (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 ); if (realObject && realObject.visible) { objects.push(realObject); } } }); } // 处理单个fabricObject(背景图层等) if (currentLayer.fabricObject && currentLayer.fabricObject.id) { const realObject = canvasObjects.find( (obj) => obj.id === currentLayer.fabricObject.id ); if (realObject && realObject.visible) { objects.push(realObject); } } // 递归处理子图层 if (currentLayer.children && Array.isArray(currentLayer.children)) { currentLayer.children.forEach((childLayer) => { if (childLayer.visible !== false) { // 只处理可见的子图层 collectLayerObjects(childLayer); } }); } }; collectLayerObjects(layer); console.log(`从图层 "${layer.name}" 收集到 ${objects.length} 个可见对象`); return objects; } /** * 使用createRasterizedImage执行抠图操作 * @param {Array} sourceObjects 源对象数组 * @param {Object} selectionObject 选区对象 * @param {Object} selectionBounds 选区边界 * @returns {String} 抠图结果的DataURL * @private */ async _performCutoutWithRasterized( sourceObjects, selectionObject, selectionBounds ) { try { console.log("=== 开始使用createRasterizedImage执行抠图 ==="); console.log(`源对象数量: ${sourceObjects.length}`); console.log(`选区边界:`, selectionBounds); // 确定缩放因子,确保高质量输出 let scaleFactor = this.baseResolutionScale; if (this.highResolutionEnabled) { const devicePixelRatio = window.devicePixelRatio || 1; scaleFactor = Math.max(scaleFactor, devicePixelRatio * 1.5); } // 使用createRasterizedImage生成栅格化图像,将选区作为裁剪路径 const rasterizedDataURL = await createRasterizedImage({ canvas: this.canvas, fabricObjects: sourceObjects, clipPath: selectionObject, // 使用选区作为裁剪路径 trimWhitespace: true, trimPadding: 2, quality: 1.0, format: "png", scaleFactor: scaleFactor, // isReturenDataURL: true, // 返回DataURL preserveOriginalQuality: true, // 启用高质量模式 selectionManager: this.selectionManager, // 传递选区管理器,用于获取羽化值 }); if (!rasterizedDataURL) { throw new Error("栅格化生成图像失败"); } console.log(`✅ 高质量抠图完成,缩放因子: ${scaleFactor}x`); return rasterizedDataURL; } catch (error) { console.error("使用createRasterizedImage执行抠图失败:", error); // 如果createRasterizedImage失败,回退到原始方法 console.log("⚠️ 回退到原始抠图方法..."); return await this._performCutout( { fabricObjects: sourceObjects }, selectionObject ); } } /** * 原始抠图方法(作为备用方案) * @param {Object} sourceLayer 源图层 * @param {Object} selectionObject 选区对象 * @returns {String} 抠图结果的DataURL * @private */ async _performCutout(sourceLayer, selectionObject) { try { console.log("=== 使用原始方法执行抠图 ==="); // 获取选区边界 const selectionBounds = selectionObject.getBoundingRect(true, true); // 保存画布当前状态 const originalActiveObject = this.canvas.getActiveObject(); const originalSelection = this.canvas.selection; // 临时禁用画布选择 this.canvas.selection = false; this.canvas.discardActiveObject(); let tempGroup = null; let originalObjects = []; try { // 收集源图层中的可见对象 const visibleObjects = sourceLayer.fabricObjects.filter( (obj) => obj.visible ); if (visibleObjects.length === 0) { throw new Error("源图层没有可见对象"); } // 如果只有一个对象且已经是组,直接使用 if (visibleObjects.length === 1 && visibleObjects[0].type === "group") { tempGroup = visibleObjects[0]; } else { // 创建临时组 const clonedObjects = []; for (const obj of visibleObjects) { const cloned = await this._cloneObject(obj); clonedObjects.push(cloned); } tempGroup = new fabric.Group(clonedObjects, { selectable: false, evented: false, }); this.canvas.add(tempGroup); } // 设置选区为裁剪路径 const clipPath = await this._cloneObject(selectionObject); clipPath.set({ fill: "", stroke: "", absolutePositioned: true, originX: "left", originY: "top", }); tempGroup.set({ clipPath: clipPath, }); this.canvas.renderAll(); // 计算渲染区域 const renderBounds = { left: selectionBounds.left, top: selectionBounds.top, width: selectionBounds.width, height: selectionBounds.height, }; // 设置高分辨率倍数 let highResolutionScale = 1; if (this.highResolutionEnabled) { const devicePixelRatio = window.devicePixelRatio || 1; highResolutionScale = Math.max( this.baseResolutionScale, devicePixelRatio * 2 ); } // 创建用于导出的高分辨率canvas const exportCanvas = document.createElement("canvas"); const exportCtx = exportCanvas.getContext("2d", { alpha: true, willReadFrequently: false, colorSpace: "srgb", }); const actualWidth = Math.round( renderBounds.width * highResolutionScale ); const actualHeight = Math.round( renderBounds.height * highResolutionScale ); exportCanvas.width = actualWidth; exportCanvas.height = actualHeight; exportCanvas.style.width = renderBounds.width + "px"; exportCanvas.style.height = renderBounds.height + "px"; exportCtx.imageSmoothingEnabled = true; exportCtx.imageSmoothingQuality = "high"; exportCtx.clearRect(0, 0, actualWidth, actualHeight); const vpt = this.canvas.viewportTransform; const zoom = this.canvas.getZoom(); exportCtx.save(); exportCtx.scale(highResolutionScale, highResolutionScale); exportCtx.translate(-renderBounds.left, -renderBounds.top); if (zoom !== 1 || vpt[4] !== 0 || vpt[5] !== 0) { exportCtx.transform(vpt[0], vpt[1], vpt[2], vpt[3], vpt[4], vpt[5]); } tempGroup.render(exportCtx); exportCtx.restore(); const dataUrl = exportCanvas.toDataURL("image/png", 1.0); return dataUrl; } finally { // 清理和恢复 if (tempGroup) { tempGroup.set({ clipPath: null }); if (originalObjects.length > 0) { this.canvas.remove(tempGroup); } } this.canvas.selection = originalSelection; if (originalActiveObject) { this.canvas.setActiveObject(originalActiveObject); } this.canvas.renderAll(); } } catch (error) { console.error("原始方法执行抠图失败:", error); return null; } } /** * 从DataURL创建fabric图像对象 * @param {String} dataUrl 图像DataURL * @param {Object} selectionBounds 选区边界信息 * @returns {fabric.Image} fabric图像对象 * @private */ async _createFabricImage(dataUrl, selectionBounds) { return new Promise((resolve, reject) => { fabric.Image.fromURL( dataUrl, (img) => { if (!img) { reject(new Error("无法从DataURL创建图像")); return; } // 使用选区的位置作为图像位置 let targetLeft = selectionBounds.left + selectionBounds.width / 2; let targetTop = selectionBounds.top + selectionBounds.height / 2; // 设置图像属性,保持选区的原始尺寸 img.set({ left: targetLeft, top: targetTop, originX: "center", originY: "center", selectable: true, evented: true, hasControls: true, hasBorders: true, cornerStyle: "circle", cornerColor: "#007aff", cornerSize: 10, transparentCorners: false, borderColor: "#007aff", borderScaleFactor: 2, // 优化图像渲染质量 objectCaching: false, // 禁用缓存以确保最佳质量 statefullCache: true, noScaleCache: false, }); // 更新坐标 img.setCoords(); console.log( `图像创建完成,位置: (${targetLeft}, ${targetTop}), 尺寸: ${img.width}x${img.height}` ); resolve(img); }, { crossOrigin: "anonymous", // 确保图像以最高质量加载 quality: 1.0, } ); }); } /** * 克隆fabric对象 * @param {Object} obj fabric对象 * @returns {Object} 克隆的对象 * @private */ async _cloneObject(obj) { return new Promise((resolve, reject) => { if (!obj) { reject(new Error("对象无效,无法克隆")); return; } try { obj.clone((cloned) => { if (cloned) { resolve(cloned); } else { reject(new Error("对象克隆失败")); } }); } catch (error) { reject(error); } }); } /** * 序列化选区对象 * @private */ _serializeSelectionObject() { try { if (!this.selectionManager) { console.warn("选区管理器不存在,无法序列化选区对象"); return; } const selectionObject = this.selectionManager.getSelectionObject(); if (!selectionObject) { console.warn("当前没有选区对象,无法序列化"); return; } // 将选区对象转换为可序列化的对象 this.serializedSelectionObject = selectionObject.toObject([ "id", "layerId", "layerName", "parentId", ]); console.log("选区对象已序列化保存"); } catch (error) { console.error("序列化选区对象失败:", error); this.serializedSelectionObject = null; } } /** * 反序列化选区对象 * @returns {Promise} 选区对象 * @private */ async _getSelectionObject() { try { // 优先使用克隆的选区对象 if (this._clonedSelectionObject) { console.log("使用克隆的选区对象"); return await this._cloneObject(this._clonedSelectionObject); } // 尝试从选区管理器获取当前选区 const currentSelection = this.selectionManager.getSelectionObject(); if (currentSelection) { console.log("从选区管理器获取到当前选区"); return currentSelection; } // 最后使用序列化数据恢复(备用方案) if (!this.serializedSelectionObject) { console.error("没有可用的选区对象数据"); return null; } console.log("从序列化数据恢复选区对象"); // 根据选区对象类型进行反序列化 return new Promise((resolve, reject) => { const objectType = this.serializedSelectionObject.type; if (objectType === "path") { fabric.Path.fromObject(this.serializedSelectionObject, (path) => { if (path) { console.log("路径选区对象反序列化成功"); resolve(path); } else { reject(new Error("路径选区对象反序列化失败")); } }); } else if (objectType === "polygon") { 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) { console.log("矩形选区对象反序列化成功"); resolve(rect); } else { reject(new Error("矩形选区对象反序列化失败")); } }); } else if (objectType === "ellipse" || objectType === "circle") { 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("通用选区对象反序列化失败")); } } ); } }); } catch (error) { console.error("获取选区对象失败:", error); return null; } } /** * 恢复原图层和其fabric对象 * @private */ async _restoreOriginalLayer() { if (!this.originalLayer) { console.warn("没有保存的原图层信息,无法恢复"); return false; } try { console.log(`↩️ 开始恢复原图层: ${this.originalLayer.name}`); // 1. 恢复图层到原位置 if (this.originalLayerIndex !== -1) { this.layerManager.layers.value.splice( this.originalLayerIndex, 0, this.originalLayer ); } else { // 如果没有保存索引,添加到末尾 this.layerManager.layers.value.push(this.originalLayer); } // 2. 恢复fabric对象到画布 if (this.originalFabricObjects.length > 0) { console.log( `↩️ 恢复 ${this.originalFabricObjects.length} 个fabric对象` ); // 使用fabric.util.enlivenObjects批量反序列化对象 await new Promise((resolve, reject) => { fabric.util.enlivenObjects( this.originalFabricObjects, (restoredObjects) => { if (!restoredObjects || restoredObjects.length === 0) { console.warn("没有成功反序列化任何对象"); resolve(); return; } // 将恢复的对象添加到画布 restoredObjects.forEach((obj) => { if (obj) { // 确保对象的图层信息正确 obj.layerId = this.originalLayer.id; obj.layerName = this.originalLayer.name; obj.setCoords(); this.canvas.add(obj); } }); console.log(`✅ 成功恢复 ${restoredObjects.length} 个fabric对象`); resolve(); }, // 命名空间,用于自定义对象类型 "" ); }); } // 3. 如果恢复的是活动图层,设置为当前活动图层 if (this.originalLayer.id === this.layerManager.activeLayerId.value) { this.layerManager.activeLayerId.value = this.originalLayer.id; } else { // 如果当前没有活动图层,将恢复的图层设为活动图层 if (!this.layerManager.activeLayerId.value) { this.layerManager.activeLayerId.value = this.originalLayer.id; } } // 4. 重新渲染画布 this.canvas.renderAll(); console.log(`✅ 原图层恢复完成: ${this.originalLayer.name}`); return true; } catch (error) { console.error("❌ 恢复原图层失败:", error); return false; } } /** * 重做操作 * @returns {Promise} 执行结果 */ async redo() { console.log(`🔄 开始重做套索抠图操作`); // 重做操作等同于重新执行,但需要先清理一些状态 this.executedCommands = []; this.newLayerId = null; this.cutoutImageUrl = null; this.fabricImage = null; this.groupLayer = null; // 重新生成groupId确保唯一性(可选,也可以复用原来的) // this.groupId = `cutout-group-${Date.now()}`; return await this.execute(); } } /** * 清除选区命令 */ export class ClearSelectionCommand extends Command { constructor(options = {}) { super({ name: "清除选区", description: "清除当前选区", saveState: false, }); this.selectionManager = options.selectionManager; // 序列化保存完整的选区状态 this.originalSelectionState = null; this._serializeSelectionState(); } async execute() { if (!this.selectionManager) { console.error("无法清除选区:参数无效"); return false; } // 在执行前再次保存选区状态(以防初始化时没有选区) if (!this.originalSelectionState) { this._serializeSelectionState(); } // 清除选区 this.selectionManager.clearSelection(); return true; } async undo() { if (!this.selectionManager || !this.originalSelectionState) return false; try { // 从序列化状态恢复选区 await this._restoreSelectionState(); return true; } catch (error) { console.error("恢复选区状态失败:", error); return false; } } /** * 重做操作(与execute相同) * @returns {Promise} 执行结果 */ async redo() { return await this.execute(); } /** * 序列化选区状态 * @private */ _serializeSelectionState() { try { if (!this.selectionManager) { console.warn("选区管理器不存在,无法序列化选区状态"); return; } const selectionObject = this.selectionManager.getSelectionObject(); if (!selectionObject) { console.log("当前没有选区对象,无需序列化"); return; } // 序列化选区对象和相关状态 this.originalSelectionState = { // 选区对象的序列化数据 selectionObjectData: selectionObject.toObject([ "id", "layerId", "layerName", "parentId", ]), // 选区路径数据 selectionPath: this.selectionManager.getSelectionPath(), // 羽化值 featherAmount: this.selectionManager.getFeatherAmount(), // 选区ID selectionId: selectionObject.id, // 选区类型 selectionType: selectionObject.type, // 选区在画布中的位置 position: { left: selectionObject.left, top: selectionObject.top, originX: selectionObject.originX, originY: selectionObject.originY, }, // 选区样式 selectionStyle: { stroke: selectionObject.stroke, strokeWidth: selectionObject.strokeWidth, strokeDashArray: selectionObject.strokeDashArray, fill: selectionObject.fill, shadow: selectionObject.shadow ? { color: selectionObject.shadow.color, blur: selectionObject.shadow.blur, offsetX: selectionObject.shadow.offsetX, offsetY: selectionObject.shadow.offsetY, } : null, }, // 选区管理器的内部状态 managerState: { isActive: this.selectionManager.isActive, currentTool: this.selectionManager.currentTool, selectionType: this.selectionManager.selectionType, }, }; console.log("选区状态已序列化保存", { selectionId: this.originalSelectionState.selectionId, selectionType: this.originalSelectionState.selectionType, featherAmount: this.originalSelectionState.featherAmount, }); } catch (error) { console.error("序列化选区状态失败:", error); this.originalSelectionState = null; } } /** * 从序列化状态恢复选区 * @private */ async _restoreSelectionState() { return new Promise((resolve, reject) => { try { if (!this.originalSelectionState) { console.warn("没有保存的选区状态"); resolve(); return; } const { selectionObjectData, featherAmount, selectionStyle, position, managerState, } = this.originalSelectionState; // 根据选区对象类型进行反序列化 const objectType = selectionObjectData.type; const restoreSelection = (restoredObject) => { if (!restoredObject) { reject(new Error("选区对象反序列化失败")); return; } try { // 恢复选区ID和基本属性 restoredObject.set({ id: this.originalSelectionState.selectionId, layerId: selectionObjectData.layerId, layerName: selectionObjectData.layerName, parentId: selectionObjectData.parentId, }); // 恢复位置信息 if (position) { restoredObject.set({ left: position.left, top: position.top, originX: position.originX || "left", originY: position.originY || "top", }); } // 恢复样式 if (selectionStyle) { restoredObject.set({ stroke: selectionStyle.stroke, strokeWidth: selectionStyle.strokeWidth, strokeDashArray: selectionStyle.strokeDashArray, fill: selectionStyle.fill, selectable: false, evented: false, excludeFromExport: true, hoverCursor: "default", moveCursor: "default", }); // 恢复阴影(羽化效果) if (selectionStyle.shadow) { restoredObject.shadow = new fabric.Shadow( selectionStyle.shadow ); } } // 确保对象坐标正确设置 restoredObject.setCoords(); // 通过选区管理器设置选区对象,这会正确更新内部状态 this.selectionManager.setSelectionObject(restoredObject); // 恢复羽化值 this.selectionManager.setFeatherAmount(featherAmount || 0); // 恢复选区管理器的内部状态 if (managerState) { // 注意:不要强制设置isActive状态,因为这可能影响工具切换 // this.selectionManager.isActive = managerState.isActive; this.selectionManager.currentTool = managerState.currentTool; this.selectionManager.selectionType = managerState.selectionType; } // 触发选区变化回调 if ( this.selectionManager.onSelectionChanged && typeof this.selectionManager.onSelectionChanged === "function" ) { this.selectionManager.onSelectionChanged(); } console.log("选区状态恢复成功", { id: restoredObject.id, type: restoredObject.type, featherAmount: featherAmount, }); resolve(); } catch (error) { console.error("设置恢复的选区对象失败:", error); reject(error); } }; // 根据对象类型进行反序列化 if (objectType === "path") { fabric.Path.fromObject(selectionObjectData, restoreSelection); } else if (objectType === "polygon") { fabric.Polygon.fromObject(selectionObjectData, restoreSelection); } else if (objectType === "rect") { fabric.Rect.fromObject(selectionObjectData, restoreSelection); } else if (objectType === "ellipse" || objectType === "circle") { fabric.Ellipse.fromObject(selectionObjectData, restoreSelection); } else { // 通用对象反序列化 fabric.util.enlivenObjects([selectionObjectData], (objects) => { if (objects && objects.length > 0) { restoreSelection(objects[0]); } else { reject(new Error("通用选区对象反序列化失败")); } }); } } catch (error) { console.error("恢复选区状态失败:", error); reject(error); } }); } /** * 验证选区状态是否正确恢复 * @private */ _validateRestoredState() { if (!this.selectionManager || !this.originalSelectionState) { return false; } const currentSelection = this.selectionManager.getSelectionObject(); if (!currentSelection) { console.warn("选区对象未正确恢复"); return false; } // 验证基本属性 const originalId = this.originalSelectionState.selectionId; if (currentSelection.id !== originalId) { console.warn( `选区ID不匹配: 期望 ${originalId}, 实际 ${currentSelection.id}` ); return false; } // 验证羽化值 const currentFeather = this.selectionManager.getFeatherAmount(); const originalFeather = this.originalSelectionState.featherAmount; if (currentFeather !== originalFeather) { console.warn( `羽化值不匹配: 期望 ${originalFeather}, 实际 ${currentFeather}` ); return false; } console.log("选区状态验证通过"); return true; } /** * 获取命令信息 * @returns {Object} 命令详细信息 */ getInfo() { return { name: this.name, description: this.description, hasOriginalState: !!this.originalSelectionState, selectionType: this.originalSelectionState?.selectionType || null, featherAmount: this.originalSelectionState?.featherAmount || 0, selectionId: this.originalSelectionState?.selectionId || null, managerState: this.originalSelectionState?.managerState || null, canUndo: !!this.originalSelectionState, canRedo: true, }; } }