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"; import { fabric } from "fabric-with-all"; import { generateId } from "../utils/helper.js"; import { ClearSelectionCommand } from "./LassoCutoutCommand.js"; import { ClearSelectionContentCommand } from "./ClearSelectionContentCommand.js"; /** * 剪切选区到新图层命令 * 实现将选区内容复制到新图层,并将复制的选区作为组的遮罩 */ export class CutSelectionToNewLayerCommand 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 || `cut-group-${Date.now()}`; this.groupName = options.groupName || `剪切组`; this.groupLayer = null; // 保存组图层的引用 this.originalLayersLength = 0; // 保存原始图层数量 // 复制的选区对象,用作遮罩 this.clippingMaskObject = null; this.sourceLayerId = null; // 保存源图层ID // 在初始化时克隆保存选区对象,避免撤销后重做时获取不到选区对象 this._clonedSelectionObject = null; this._initializeClonedSelection(); } /** * 初始化克隆的选区对象 * @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", ]); } } } } 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; } // 保存源图层ID this.sourceLayerId = sourceLayer.id; // 获取源图层的所有对象(包括子图层) const sourceObjects = this._getLayerObjects(sourceLayer); if (sourceObjects.length === 0) { console.error("无法执行剪切选区:源图层没有可见对象"); return false; } // 保存原图层状态用于撤销 await this._backupOriginalLayer(sourceLayer, sourceObjects); // 获取选区边界信息用于后续定位 const selectionBounds = selectionObject.getBoundingRect(true, true); // 步骤1: 先创建抠图到新图层(复制选区内容) this.fabricImage = await this._performCutoutWithRasterized( sourceObjects, selectionObject, selectionBounds ); if (!this.fabricImage) { console.error("抠图失败"); return false; } // 步骤2: 对原图层进行栅格化处理,移除选区内容 await this._rasterizeOriginalLayerWithCutout( sourceLayer, sourceObjects, selectionObject ); // 步骤3: 创建图像图层命令 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); // 步骤4: 创建组图层并设置剪切结果为遮罩 const topLayerIndex = this.layerManager.layers.value.findIndex( (layer) => layer.id === this.newLayerId ); const selectLayer = this.layerManager.layers.value[topLayerIndex]; // 创建新的组图层 this.groupLayer = createLayer({ id: this.groupId, name: this.groupName || `剪切组`, type: LayerType.GROUP, visible: true, locked: false, opacity: 1.0, fabricObjects: [], children: [], }); this.fabricImage.set({ selectable: true, evented: true, }); selectLayer.parentId = this.groupId; selectLayer.fabricObjects = [ this.fabricImage.toObject("id", "layerId", "layerName", "parentId"), ]; this.groupLayer.clippingMask = this.fabricImage.toObject( "id", "layerId", "layerName", "parentId" ); this.groupLayer.children.push(selectLayer); // 插入新组图层 this.layerManager.layers.value.splice(topLayerIndex, 1, this.groupLayer); 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, groupId: this.groupId, groupName: this.groupName, }; } catch (error) { console.error("剪切选区过程中出错:", error); // 错误清理和回滚 await this._handleExecutionError(); 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) { this.layerManager.layers.value.splice(groupIndex, 1); console.log(`移除了组图层: ${this.groupId}`); } } if (this.fabricImage) { // 从画布移除抠图对象 if (this.canvas.getObjects().includes(this.fabricImage)) { this.canvas.remove(this.fabricImage); } } // 2. 恢复原图层状态(关键:在撤销子命令之前恢复原图层) if (this.originalLayerRasterized) { await this._restoreOriginalLayer(); } // 3. 逆序撤销所有已执行的子命令 for (let i = this.executedCommands.length - 1; i >= 0; i--) { const command = this.executedCommands[i]; try { if (command && typeof command.undo === "function") { await command.undo(); console.log(`✅ 子命令撤销成功: ${command.constructor.name}`); } } catch (error) { console.error( `❌ 子命令撤销失败: ${command.constructor.name}`, error ); // 子命令撤销失败不中断整个撤销过程 } } // 4. 清理状态 this.executedCommands = []; this.newLayerId = null; this.cutoutImageUrl = null; this.fabricImage = null; this.groupLayer = null; // 重置栅格化标记 this.originalLayerRasterized = false; this.rasterizedOriginalImage = null; // 5. 更新画布和图层交互性 await this.layerManager.updateLayersObjectsInteractivity(true); console.log(`✅ 剪切选区撤销完成`); return true; } catch (error) { console.error("❌ 撤销剪切选区失败:", error); return false; } } /** * 恢复原图层状态 - 关键修复方法 * @private */ async _restoreOriginalLayer() { try { console.log("🔄 开始恢复原图层状态..."); if (!this.originalLayerBackup || !this.sourceLayerId) { console.warn("没有原图层备份数据或源图层ID"); return; } // 1. 移除栅格化后的图像 if (this.rasterizedOriginalImage) { if (this.canvas.getObjects().includes(this.rasterizedOriginalImage)) { this.canvas.remove(this.rasterizedOriginalImage); console.log("移除了栅格化图像"); } } // 2. 获取当前的源图层引用 const sourceLayer = this.layerManager.getLayerById(this.sourceLayerId); if (!sourceLayer) { console.error(`找不到源图层: ${this.sourceLayerId}`); return; } // 3. 清空当前图层的对象列表 sourceLayer.fabricObjects = []; // 4. 恢复原始对象数据到图层 if (this.originalLayerObjectsData.length > 0) { // 使用备份的序列化数据重建对象 await this._restoreObjectsFromBackup(sourceLayer); } else { // 备用方案:恢复原始的fabricObjects数组 sourceLayer.fabricObjects = this.originalLayerBackup.fabricObjects || []; if (sourceLayer.fabricObjects.length > 0) { await this._restoreLayerObjects(sourceLayer); } } console.log( `✅ 原图层状态恢复完成,恢复了 ${sourceLayer.fabricObjects.length} 个对象` ); } catch (error) { console.error("恢复原图层状态失败:", error); throw error; } } /** * 从备份数据恢复对象 - 修复版本 * @param {Object} layer 目标图层 * @private */ async _restoreObjectsFromBackup(layer) { return new Promise((resolve, reject) => { if ( !this.originalLayerObjectsData || this.originalLayerObjectsData.length === 0 ) { console.warn("没有对象备份数据"); resolve(); return; } try { console.log( `开始从备份恢复 ${this.originalLayerObjectsData.length} 个对象...` ); // 使用fabric.util.enlivenObjects重建对象 fabric.util.enlivenObjects( this.originalLayerObjectsData, (restoredObjects) => { try { let successCount = 0; restoredObjects.forEach((obj, index) => { if (obj) { // 确保对象有正确的属性 obj.set({ layerId: layer.id, layerName: layer.name, selectable: true, evented: true, visible: true, }); // 添加到画布 this.canvas.add(obj); // 添加到图层的fabricObjects数组 layer.fabricObjects.push( obj.toObject([ "id", "layerId", "layerName", "parentId", "selectable", "evented", "visible", ]) ); successCount++; console.log( `恢复对象 ${index + 1}/${restoredObjects.length}: ${ obj.id || obj.type }` ); } else { console.warn(`对象 ${index + 1} 恢复失败`); } }); // 重新渲染画布 this.canvas.renderAll(); console.log( `✅ 成功恢复了 ${successCount}/${this.originalLayerObjectsData.length} 个对象` ); resolve(); } catch (error) { console.error("处理恢复的对象时出错:", error); reject(error); } } ); } catch (error) { console.error("恢复对象时出错:", error); reject(error); } }); } /** * 重做操作 * @returns {Promise} 执行结果 */ async redo() { try { console.log(`🔄 开始重做剪切选区操作`); // 重做时重新执行整个操作 const result = await this.execute(); if (result) { console.log(`✅ 剪切选区重做完成`); return true; } else { console.log(`❌ 剪切选区重做失败`); return false; } } 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, subCommands: this.executedCommands.map((cmd) => ({ name: cmd.constructor.name, info: cmd.getInfo ? cmd.getInfo() : {}, })), }; } /** * 使用createRasterizedImage执行抠图操作 * @param {Array} sourceObjects 源对象数组 * @param {Object} selectionObject 选区对象 * @param {Object} selectionBounds 选区边界 * @returns {fabric.Image} 抠图结果的fabric图像对象 * @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 currentZoom = this.canvas.getZoom?.() || 1; scaleFactor = Math.max( scaleFactor || this.canvas?.getRetinaScaling?.(), currentZoom ); scaleFactor = Math.min(scaleFactor, 3); } // 使用createRasterizedImage生成栅格化图像,将选区作为裁剪路径 const rasterizedImage = await createRasterizedImage({ canvas: this.canvas, fabricObjects: sourceObjects, clipPath: selectionObject, trimWhitespace: true, trimPadding: 2, quality: 1.0, format: "png", scaleFactor: scaleFactor, preserveOriginalQuality: true, selectionManager: this.selectionManager, }); if (!rasterizedImage) { console.error("高质量剪切抠图失败"); return null; } console.log(`✅ 高质量剪切抠图完成,缩放因子: ${scaleFactor}x`); return rasterizedImage; } catch (error) { console.error("使用createRasterizedImage执行剪切抠图失败:", error); throw error; } } /** * 获取图层的所有对象(包括子图层,从画布中查找真实对象) * @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((fabricObj) => { if (fabricObj && fabricObj.id) { const realObject = canvasObjects.find( (canvasObj) => canvasObj.id === fabricObj.id ); if (realObject && realObject.visible !== false) { objects.push(realObject); } } }); } // 处理单个fabricObject(背景图层等) if (currentLayer.fabricObject && currentLayer.fabricObject.id) { const realObject = canvasObjects.find( (canvasObj) => canvasObj.id === currentLayer.fabricObject.id ); if (realObject && realObject.visible !== false) { objects.push(realObject); } } // 递归处理子图层 if (currentLayer.children && Array.isArray(currentLayer.children)) { currentLayer.children.forEach((childLayer) => { collectLayerObjects(childLayer); }); } }; collectLayerObjects(layer); console.log(`从图层 "${layer.name}" 收集到 ${objects.length} 个可见对象`); return objects; } /** * 序列化选区对象 * @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对象 * @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); } }); } /** * 备份原图层状态 * @param {Object} sourceLayer 源图层 * @param {Array} sourceObjects 源对象数组 * @private */ async _backupOriginalLayer(sourceLayer, sourceObjects) { try { console.log("🔄 备份原图层状态..."); // 深度复制图层对象 this.originalLayerBackup = JSON.parse(JSON.stringify(sourceLayer)); // 备份图层中的所有对象数据 this.originalLayerObjectsData = []; for (const obj of sourceObjects) { if (obj && obj.toObject) { const objData = obj.toObject([ "id", "layerId", "layerName", "parentId", "selectable", "evented", "visible", ]); this.originalLayerObjectsData.push(objData); } } this.originalLayerRasterized = false; // 标记原图层是否已栅格化 console.log( `✅ 原图层状态已备份,对象数量: ${this.originalLayerObjectsData.length}` ); } catch (error) { console.error("备份原图层状态失败:", error); throw error; } } /** * 对原图层进行栅格化处理,移除选区内容 * @param {Object} sourceLayer 源图层 * @param {Array} sourceObjects 源对象数组 * @param {Object} selectionObject 选区对象 * @private */ async _rasterizeOriginalLayerWithCutout( sourceLayer, sourceObjects, selectionObject ) { try { console.log("=== 开始对原图层进行栅格化处理,移除选区内容 ==="); // 克隆选区对象,确保不影响原对象 const clonedSelectionObject = await this._cloneObject(selectionObject); // 直接使用 ClearSelectionContentCommand 的正确实现,传入选区对象 const clearCommand = new ClearSelectionContentCommand({ canvas: this.canvas, layerManager: this.layerManager, selectionManager: this.selectionManager, selectionObject: clonedSelectionObject, // 传入选区对象 highResolutionEnabled: this.highResolutionEnabled, baseResolutionScale: this.baseResolutionScale, }); // 临时设置目标图层为当前源图层 clearCommand.targetLayerId = sourceLayer.id; clearCommand.targetLayer = sourceLayer; // 执行清除选区内容命令 await clearCommand.execute(); // 获取清除后的栅格化图像 this.rasterizedOriginalImage = clearCommand.rasterizedImage; this.originalLayerRasterized = true; // 标记原图层已栅格化 console.log("✅ 原图层栅格化处理完成,选区内容已移除"); } catch (error) { console.error("原图层栅格化处理失败:", error); throw error; } } /** * 创建反向剪切路径(选区外的内容) * @param {Object} selectionObject 选区对象 * @param {Array} sourceObjects 源对象数组 * @returns {Object} 反向剪切路径 * @private */ async _createInvertedClipPath(selectionObject, sourceObjects) { try { // 计算所有源对象的边界 let minLeft = Infinity, minTop = Infinity, maxRight = -Infinity, maxBottom = -Infinity; sourceObjects.forEach((obj) => { const bounds = obj.getBoundingRect(true, true); minLeft = Math.min(minLeft, bounds.left); minTop = Math.min(minTop, bounds.top); maxRight = Math.max(maxRight, bounds.left + bounds.width); maxBottom = Math.max(maxBottom, bounds.top + bounds.height); }); // 扩展边界以确保完整覆盖 const padding = 50; const canvasBounds = { left: minLeft - padding, top: minTop - padding, width: maxRight - minLeft + padding * 2, height: maxBottom - minTop + padding * 2, }; // 创建画布大小的矩形 const canvasRect = new fabric.Rect({ left: canvasBounds.left, top: canvasBounds.top, width: canvasBounds.width, height: canvasBounds.height, fill: "rgba(255,255,255,1)", stroke: "", selectable: false, evented: false, absolutePositioned: true, originX: "left", originY: "top", }); // 克隆选区对象作为要移除的部分 const cutoutPath = await this._cloneObject(selectionObject); cutoutPath.set({ fill: "rgba(0,0,0,1)", // 黑色表示要移除的区域 stroke: "", absolutePositioned: true, globalCompositeOperation: "destination-out", // 关键:使用destination-out混合模式 }); // 创建复合路径:画布矩形 - 选区 = 反向选区 const invertedClipPath = new fabric.Group([canvasRect, cutoutPath], { selectable: false, evented: false, absolutePositioned: true, }); return invertedClipPath; } catch (error) { console.error("创建反向剪切路径失败:", error); return null; } } }