import { BackgroundFillManager } from "./BackgroundFillManager"; import { AddLayerCommand, PasteLayerCommand, RemoveLayerCommand, MoveLayerCommand, ToggleLayerVisibilityCommand, RenameLayerCommand, LayerLockCommand, SetLayerOpacityCommand, SetLayerBlendModeCommand, MergeLayersCommand, GroupLayersCommand, UngroupLayersCommand, MergeLayerObjectsCommand, LayerObjectsToGroupCommand, ReorderLayersCommand, ToggleChildLayerVisibilityCommand, RenameChildLayerCommand, RemoveChildLayerCommand, ChildLayerLockCommand, } from "../commands/LayerCommands"; import { SetActiveLayerCommand, AddObjectToLayerCommand, RemoveObjectFromLayerCommand, SelectAllLayersCommand, ClearSelectionCommand, MoveLayerToTopCommand, MoveLayerToBottomCommand, } from "../commands/ObjectLayerCommands"; import { LayerType, BlendMode, createLayer, createBackgroundLayer, createFixedLayer, OperationType, OperationTypes, findLayerRecursively, } from "../utils/layerHelper"; import { CreateBackgroundLayerCommand, UpdateBackgroundCommand, BackgroundSizeCommand, BackgroundSizeWithScaleCommand, } from "../commands/BackgroundCommands"; import { MergeGroupLayerCommand } from "../commands/GroupCommands"; import { ExportLayerToImageCommand, RasterizeLayerCommand, } from "../commands/RasterizeLayerCommand"; // 导入图层排序相关类和混入 import { LayerSort, createLayerSort, LayerSortMixin, LayerSortUtils, } from "../utils/LayerSort"; import CanvasConfig from "../config/canvasConfig"; import { isBoolean, template } from "lodash-es"; import { findObjectById, generateId, insertObjectAtZIndex, optimizeCanvasRendering, } from "../utils/helper"; import { message } from "ant-design-vue"; import { fabric } from "fabric-with-all"; import { getOriginObjectInfo } from "../utils/layerUtils"; import { restoreFabricObject } from "../utils/objectHelper"; import { UpdateGroupMaskPositionCommand } from "../commands/UpdateGroupMaskPositionCommand"; // import { useI18n } from 'vue-i18n' // const {t} = useI18n() /** * 图层管理器 - 负责管理画布上的所有图层 * 包含图层的创建、删除、修改、排序等操作 * 现在统一使用命令管理器进行状态管理 * 集成LayerSort高级排序功能 */ export class LayerManager { /** * 创建图层管理器 * @param {Object} options 配置选项 * @param {Object} options.canvas fabric.js画布实例 * @param {Object} options.layers 图层数组响应式引用 * @param {Object} options.activeLayerId 当前激活图层ID响应式引用 * @param {Object} options.commandManager 命令管理器 * @param {Object} options.canvasManager 画布管理器 * @param {String} options.editorMode 编辑器模式 * @param {Number} options.canvasWidth 画布宽度 * @param {Number} options.canvasHeight 画布高度 * @param {String} options.backgroundColor 背景颜色 * @param {Function} options.t 国际化函数 */ constructor(options) { this.canvas = options.canvas; this.layers = options.layers; this.activeLayerId = options.activeLayerId; this.commandManager = options.commandManager; this.canvasManager = options.canvasManager || null; this.lastSelectLayerId = options.lastSelectLayerId || { value: null }; // 上次选中的图层ID this.t = options.t || ((key) => key); // 国际化函数,默认为直接返回key this.backgroundFillManager = new BackgroundFillManager({ canvas: this.canvas, layers: this.layers, commandManager: this.commandManager, canvasManager: this.canvasManager, layerManager: this, }); // 编辑器模式:draw(绘画)、select(选择)、pan(拖拽) this.editorMode = options.editorMode || CanvasConfig.defaultTool; // 画布尺寸 this.canvasWidth = options.canvasWidth || 800; this.canvasHeight = options.canvasHeight || 600; // 默认背景颜色 this.backgroundColor = options.backgroundColor || { value: "#ffffff" }; // 复制粘贴相关 this.clipboardData = null; // 操作状态标志,用于控制状态保存 this.isExecutingCommand = false; // 是否正在执行操作 this.operationInProgress = false; // 红绿图模式相关 this.isRedGreenMode = options.isRedGreenMode || false; this.redGreenModeManager = null; // 初始化图层排序工具 this.layerSort = null; this.initLayerSort(); // 初始化相关命令 this.initCommandManager(); } /** * 初始化命令管理器 * 现在直接使用命令类,不再需要注册命令 */ initCommandManager() { // 命令注册逻辑已移除,现在直接使用命令类实例化和执行 console.log("CommandManager 已初始化,使用直接命令调用模式"); } /** * 初始化图层排序工具 * 创建LayerSort实例并绑定到LayerManager */ initLayerSort() { if (this.canvas && this.layers) { this.layerSort = createLayerSort(this.canvas, this.layers, { commandManager: this.commandManager, }); console.log("图层排序工具已初始化"); } } /** * 设置编辑器模式 * @param {string} mode 'draw'、'select'或'pan' */ toolChanged(mode) { if (!OperationTypes.includes(mode)) { console.warn(`不支持的编辑器模式: ${mode}`); return; } this.editorMode = mode; // 更新所有对象的交互性 this.updateLayersObjectsInteractivity(); console.log(`已切换到${mode}模式`); } setToolManager(toolManager) { this.toolManager = toolManager; } /** * 更新所有画布对象的交互性 * 根据当前编辑模式和图层状态设置对象的交互属性 * @private */ async updateLayersObjectsInteractivity( isUseOptimize = true, { isMoveing = false } = {} ) { if (!this.canvas) return; if (isUseOptimize) { // 优化渲染 - 统一批处理 支持异步回调 await optimizeCanvasRendering(this.canvas, async () => { // 应用图层交互规则 await this._applyInteractionRules({ isMoveing }); }); } else { // 直接应用图层交互规则 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(() => { // // 暂停渲染以提高性能 // this.canvas.skipTargetFind = true; // // this.canvas.discardActiveObject(); // // 暂停实时渲染和对象查找 // const wasRenderOnAddRemove = this.canvas.renderOnAddRemove; // this.canvas.renderOnAddRemove = false; // // 应用图层交互规则 // this._applyInteractionRules(); // // 恢复渲染设置 // this.canvas.renderOnAddRemove = wasRenderOnAddRemove; // this.canvas.skipTargetFind = false; // // this.canvas.renderAll(); // this.canvas.renderAll(); // 确保画布重新渲染 - 同步渲染 // }); } async _setObjectInteractivity(obj, layer, editorMode) { // 设置可见性 obj.visible = layer.visible; // 判断对象是否在当前活动图层上 const isInActiveLayer = obj.layerId === this.activeLayerId.value; // 基于 fabric-with-erasing 库的 erasable 属性设置擦除权限 // 只有活动图层、可见、非锁定、非背景、非固定图层的对象才可擦除 obj.erasable = isInActiveLayer && layer.visible && !layer.locked && !layer.isBackground && !layer.isFixed; // 图层状态决定交互性 if (layer.isBackground || obj.isBackground || layer.isFixed) { //|| layer.isFixed // 背景层永远不可选择和擦除 obj.selectable = false; obj.evented = false; obj.erasable = !layer.locked; } else if (layer.locked) { // 锁定图层不可交互和擦除 obj.selectable = false; obj.evented = false; obj.erasable = false; } else { // 根据编辑模式设置交互性 switch (editorMode) { case OperationType.SELECT: obj.selectable = true; obj.evented = true; break; case OperationType.ERASER: // 橡皮擦模式:利用 fabric-with-erasing 的内置机制 // 只需要设置 erasable 属性,库会自动处理擦除逻辑 obj.selectable = false; obj.evented = true; // 需要设置为 true 以接收鼠标事件 // erasable 已在上面根据图层状态设置 break; case OperationType.DRAW: case OperationType.EYEDROPPER: case OperationType.PAN: case OperationType.WAVE: case OperationType.LIQUIFY: case OperationType.LASSO: case OperationType.LASSO_RECTANGLE: case OperationType.AREA_CUSTOM: case OperationType.AREA_RECTANGLE: obj.selectable = false; obj.evented = false; break; default: obj.selectable = false; obj.evented = false; } if (this.isRedGreenMode) { // 红绿图模式下 所有普通图层都可擦除 obj.erasable = layer.visible && !layer.locked && !layer.isBackground && !layer.isFixed; } // 平移模式下,禁用多选和擦除 if (editorMode === OperationType.PAN) { obj.selectable = false; obj.evented = false; obj.erasable = false; } } // 应用图层视觉属性 if (layer.opacity !== undefined) obj.opacity = layer.opacity; if (layer.blendMode) obj.globalCompositeOperation = layer.blendMode; } // 私有方法:应用交互规则 async _applyInteractionRules({ isMoveing }) { console.log("updateLayersObjectsInteractivity ===>", this.editorMode); const objects = this.canvas.getObjects(); const editorMode = this.editorMode || CanvasConfig.defaultTool; const layers = this.layers?.value || []; // 创建缓存以避免重复查找 const layerMap = {}; layers.forEach((layer) => { layerMap[layer.id] = layer; layer?.children?.forEach((childLayer) => { layerMap[childLayer.id] = childLayer; }); }); // 批量更新对象 objects.forEach(async (obj) => { const layer = layerMap[obj.layerId]; if (!obj.layerId) { // 没有关联图层的对象使用默认设置 obj.selectable = false; obj.evented = false; obj.erasable = false; // 未关联图层的对象不可擦除 return; } if (!layer) return; // 设置一级图层对象的交互性 await this._setObjectInteractivity(obj, layer, editorMode); // 设置子图层对象的交互性 layer?.children?.forEach(async (childLayer) => { const childObj = this.canvas .getObjects() .find((o) => o.layerId === childLayer.id); if (childObj) { await this._setObjectInteractivity(childObj, childLayer, editorMode); } }); }); // 设置裁剪对象 layers.forEach(async (layer) => { let clippingMaskFabricObject = null; if (layer.clippingMask) { // 反序列化 clippingMask clippingMaskFabricObject = await restoreFabricObject( layer.clippingMask, this.canvas ); clippingMaskFabricObject.clipPath = null; clippingMaskFabricObject.set({ // 设置绝对定位 // ...getOriginObjectInfo(layer.clippingMask), // 恢复原定位 absolutePositioned: true, }); } // 如果是组图层 则给所有子对象设置裁剪对象 if (layer.type === LayerType.GROUP || layer.children?.length > 0) { // if (layer.fill) { // const fabricObject = this.canvas.getObjects().find((o) => o.id === layer.fill.id); // if (fabricObject) { // fabricObject.clipPath = clippingMaskFabricObject; // fabricObject.dirty = true; // 标记为脏对象 // fabricObject.setCoords(); // } // } layer.children.forEach((childLayer) => { const childObj = this.canvas .getObjects() .find((o) => o.layerId === childLayer.id); if (childObj) { childObj.clipPath = clippingMaskFabricObject; childObj.dirty = true; // 标记为脏对象 childObj.setCoords(); } // if (childLayer.fill) { // const fabricObject = this.canvas.getObjects().find((o) => o.id === childLayer.fill.id); // if (fabricObject) { // fabricObject.clipPath = clippingMaskFabricObject; // fabricObject.dirty = true; // 标记为脏对象 // fabricObject.setCoords(); // } // } }); } else { layer.fabricObjects?.forEach((obj) => { const fabricObject = this.canvas .getObjects() .find((o) => o.id === obj.id); if (fabricObject) { fabricObject.clipPath = clippingMaskFabricObject; fabricObject.dirty = true; // 标记为脏对象 fabricObject.setCoords(); } }); // if (layer.fill) { // const fabricObject = this.canvas.getObjects().find((o) => o.id === layer.fill.id); // if (fabricObject) { // fabricObject.clipPath = clippingMaskFabricObject; // fabricObject.dirty = true; // 标记为脏对象 // fabricObject.setCoords(); // } // } } // 设置画布选区内容 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 ); } }); } /** * 填充图层背景 * @param {string} layerId 图层ID * @param {string} fillColor 填充颜色 * @param {boolean} undoable 是否可撤销 */ async fillLayerBackground(layerId, fillColor, undoable = true) { layerId = this.activeLayerId.value || layerId; await this.backgroundFillManager.fillLayerBackground( layerId, fillColor, undoable ); } /** * 创建新图层 * @param {string} name 图层名称 * @param {string} type 图层类型 * @param {Object} options 额外选项 * @returns {string} 新创建的图层ID */ async createLayer(name = null, type = LayerType.EMPTY, options = {}) { // 生成唯一ID const layerId = options.id || options.layerId || generateId("layer_"); // 计算普通图层数量(非背景、非固定) const normalLayersCount = this.layers.value.filter( (layer) => !layer.isBackground && !layer.isFixed ).length; // 计算插入位置,如果没有指定insertIndex,则根据当前选中图层决定插入位置 // 添加到图层列表 // let insertIndex = this._getInsertIndexAboveActiveLayer(); // if (options.insertIndex !== undefined) { // insertIndex = options.insertIndex; // } // 创建新图层 const newLayer = createLayer({ id: layerId, name: name || `图层 ${normalLayersCount + 1}`, type: type, visible: true, locked: false, opacity: 1.0, blendMode: BlendMode.NORMAL, fabricObjects: [], children: [], ...options, }); // 直接创建和执行命令 const command = new AddLayerCommand({ canvas: this.canvas, layers: this.layers, newLayer: newLayer, activeLayerId: this.activeLayerId, options, }); // 如果有额外选项,设置命令的undoable属性 command.undoable = options.undoable; // 如果是第一个图层,或者普通图层数量小于等于3,设置为不可撤销 if (normalLayersCount < 1) { command.undoable = false; } // 执行命令 if (this.commandManager) { await this.commandManager.execute(command); } else { await command.execute(); } return layerId; } /** * 创建背景图层 * @param {string} name 图层名称 * @returns {string} 创建的背景层ID */ createBackgroundLayer(name = "背景") { // 检查是否已有背景图层 const hasBackgroundLayer = this.layers.value.some( (layer) => layer.isBackground ); if (hasBackgroundLayer) { console.warn("已存在背景层,不再创建新的背景层"); return null; } // 创建背景图层 const bgLayer = createBackgroundLayer({ name: name, canvasWidth: this.canvasWidth, canvasHeight: this.canvasHeight, backgroundColor: this.backgroundColor.value, }); // 直接创建和执行命令 const command = new CreateBackgroundLayerCommand({ canvas: this.canvas, layers: this.layers, activeLayerId: this.activeLayerId, canvasManager: this.canvasManager, backgroundLayer: bgLayer, }); // 背景图层设置为不可撤销 command.undoable = false; // 执行命令 if (this.commandManager) { this.commandManager.execute(command); } else { command.execute(); } // 返回创建的背景层ID return bgLayer.id; } /** * 创建固定图层 - 位于背景图层之上,普通图层之下 * @param {string} name 图层名称 * @returns {string} 创建的固定图层ID */ createFixedLayer(name = "固定图层") { // 检查是否已有固定图层 const hasFixedLayer = this.layers.value.some((layer) => layer.isFixed); if (hasFixedLayer) { console.warn("已存在固定图层,不再创建新的固定图层"); return null; } // 生成唯一ID const layerId = `fixed_layer_${Date.now()}_${Math.floor( Math.random() * 1000 )}`; // 创建固定图层 const fixedLayer = createFixedLayer({ id: layerId, name: name, }); // 直接创建和执行命令 const command = new AddLayerCommand({ canvas: this.canvas, layers: this.layers, newLayer: fixedLayer, activeLayerId: this.activeLayerId, insertIndex: this._getFixedLayerInsertionIndex(), }); // 固定图层设置为不可撤销 command.undoable = false; // 执行命令 if (this.commandManager) { this.commandManager.execute(command); } else { command.execute(); } return layerId; } /** * 获取应该插入固定图层的位置索引(在背景图层之上) * @private * @returns {Number} 插入索引 */ _getFixedLayerInsertionIndex() { // 找到背景图层的位置 const bgIndex = this.layers.value.findIndex((layer) => layer.isBackground); if (bgIndex !== -1) { return bgIndex; // 插入到背景图层之前(在数组中这意味着位于背景图层之上) } return this.layers.value.length; // 如果没有背景图层,则添加到最后 } /** * 初始化图层,确保有背景层、固定图层和一个空白图层 */ async initializeLayers() { // 如果没有任何图层,创建背景层、固定图层和一个空白图层 if (this.layers.value.length === 0) { // 创建背景图层 this.createBackgroundLayer(this.t("Canvas.Background")); // 创建固定图层,位于背景图层之上 this.createFixedLayer(this.t("Canvas.FixedLayer")); // 创建一个空白图层(默认位于背景图层和固定图层之上) await this.createLayer(this.t("Canvas.Layer1")); } else { // 检查是否已有背景层 const hasBackgroundLayer = this.layers.value.some( (layer) => layer.isBackground ); if (!hasBackgroundLayer) { this.createBackgroundLayer(this.t("Canvas.Background")); } // 检查是否已有固定图层 const hasFixedLayer = this.layers.value.some((layer) => layer.isFixed); if (!hasFixedLayer) { this.createFixedLayer(); } // 检查是否至少有一个普通图层(非背景、非固定) const hasNormalLayer = this.layers.value.some( (layer) => !layer.isBackground && !layer.isFixed ); if (!hasNormalLayer) { await this.createLayer(this.t("Canvas.Layer1")); } } // 排序图层 this.sortLayers(); // 更新对象交互性 this.updateLayersObjectsInteractivity(); } /** * 添加对象到图层 * @param {Object} fabricObject fabric对象 * @param {string} layerId 目标图层ID,如果不提供则使用当前活动图层 * @returns {Object} 添加的对象 */ addObjectToLayer(fabricObject, layerId = null, options = {}) { const targetLayerId = layerId || this.activeLayerId.value; // 如果没有指定图层ID,也没有活动图层,则返回错误 if (!targetLayerId) { console.warn("没有指定目标图层ID且没有活动图层,无法添加对象"); return null; } // 验证目标图层是否存在 const { layer: targetLayer } = findLayerRecursively( this.layers.value, targetLayerId ); if (!targetLayer) { console.error(`目标图层 ${targetLayerId} 不存在`); return null; } // 直接创建和执行命令 const command = new AddObjectToLayerCommand({ canvas: this.canvas, layers: this.layers, layerId: targetLayerId, fabricObject: fabricObject, layerManager: this, }); // 设置命令的撤销状态 if (isBoolean(options.undoable)) command.undoable = options.undoable; // 是否撤销 // 执行命令 if (this.commandManager) { this.commandManager.execute(command); } else { command.execute(); } return fabricObject; } /** * 向固定图层添加对象 * @param {Object} fabricObject fabric对象 * @returns {Object|null} 添加的对象或null(如果添加失败) */ addObjectToFixedLayer(fabricObject) { // 查找固定图层 const fixedLayer = this.layers.value.find((layer) => layer.isFixed); // 如果没有固定图层,则创建一个 if (!fixedLayer) { const fixedLayerId = this.createFixedLayer(); return this.addObjectToLayer(fabricObject, fixedLayerId); } // 添加对象到固定图层 return this.addObjectToLayer(fabricObject, fixedLayer.id); } /** * 从图层中移除对象 * @param {string|Object} objectOrId 要移除的对象或其ID * @returns {boolean} 是否移除成功 */ removeObjectFromLayer(objectOrId) { // 获取对象ID const objectId = typeof objectOrId === "string" ? objectOrId : objectOrId.id; if (!objectId) { console.error("无效的对象ID"); return false; } // 直接创建和执行命令 const command = new RemoveObjectFromLayerCommand({ canvas: this.canvas, layers: this.layers, objectId: objectId, objectOrId: objectOrId, }); // 执行命令 if (this.commandManager) { return this.commandManager.execute(command); } else { return command.execute(); } } /** * 设置活动图层 * @param {string} layerId 图层ID */ setActiveLayer(layerId, options = {}) { // this.lastSelectLayerId.value = layerId; // 更新最后选择的图层ID if (layerId === this.activeLayerId.value) { console.warn("当前图层已是活动图层,无需重复设置"); return; } // 直接创建和执行命令 const command = new SetActiveLayerCommand({ layers: this.layers, canvas: this.canvas, layerManager: this, activeLayerId: this.activeLayerId, layerId: layerId, parentId: options?.parentId, editorMode: this.editorMode, ...options, }); // 如果有额外选项,设置命令的undoable属性 command.undoable = !!options.undoable; // 执行命令 if (this.commandManager) { this.commandManager.execute(command); } else { command.execute(); } } /** * 获取当前活动图层 * @return {Object} layer 图层对象 */ getActiveLayer() { if (!this.activeLayerId.value) { console.warn( "没有活动图层ID,无法获取活动图层 ==== 默认设置第一个图层为活动图层" ); this.activeLayerId.value = this.layers.value[0]?.id || null; } try { const { layer: activeLayer } = findLayerRecursively( this.layers.value, this.activeLayerId.value ); if (activeLayer) { return activeLayer; } else { console.warn("没有活动图层"); return null; } } catch (error) { console.error("获取活动图层失败:", error); return null; } } // 获取当前活动图层ID getActiveLayerId() { return this.activeLayerId.value; } /** * 根据ID获取图层 * @param {string} layerId 图层ID * @returns {Object|null} 图层对象或null */ getLayerById(layerId) { const { layer } = findLayerRecursively( this.layers?.value ?? this.layers, layerId ); return layer; } /** * 根据name获取图层 * @param {string} layerName 图层名称 * @returns {Object|null} 图层对象或null */ getLayerByName(layerName) { const layer = this.layers.value.find((layer) => layer.name === layerName); return layer || null; } /** * 获取当前图层对象的列表 * @param {string} layerId 可选,指定图层ID,默认使用当前活动图层 * @returns {Array} 图层中的对象列表 */ getLayerObjects(layerId = null) { const targetLayerId = layerId || this.activeLayerId.value; if (!targetLayerId) return []; const layer = this.getLayerById(targetLayerId); if (!layer) return []; // 如果是背景图层且有单个对象 if (layer.isBackground && layer.fabricObject) { return [layer.fabricObject]; } // 普通图层返回对象列表 return Array.isArray(layer.fabricObjects) ? layer.fabricObjects : []; } /** * 移除图层 * @param {string} layerId 图层ID * @returns {boolean} 是否移除成功 */ removeLayer(layerId) { // 查找要删除的图层 const { layer, parent } = findLayerRecursively(this.layers.value, layerId); // 如果是背景层或固定层,不允许删除 if (layer && (layer.isBackground || layer.isFixed)) { console.warn(layer.isBackground ? "背景层不可删除" : "固定层不可删除"); message.warning(layer.isBackground ? "背景层不可删除" : "固定层不可删除"); return false; } // 检查是否是唯一的普通图层 const normalLayers = this.layers.value.filter((l) => !l.isBackground && !l.isFixed); console.log("普通图层:", normalLayers) if (normalLayers.length === 1) { console.warn("不能删除唯一的普通图层"); message.warning("不能删除唯一的普通图层"); return false; } // // 如果图层有子图层,提示确认 // if (layer && layer.children && layer.children.length > 0) { // console.warn("该图层包含子图层,删除将同时删除所有子图层"); // message.warning("该图层包含子图层,删除将同时删除所有子图层"); // } // 删除的是子图层 if (parent && layer) { this.removeChildLayer(layer.id, parent.id); return true; } // 直接创建和执行命令 const command = new RemoveLayerCommand({ canvas: this.canvas, layers: this.layers, layerId: layerId, activeLayerId: this.activeLayerId, layerManager: this, }); // 执行命令 if (this.commandManager) { this.commandManager.execute(command); } else { command.execute(); } return true; } updateLayerObject(layerId, fabricObject) { // 查找图层 const { layer } = findLayerRecursively(this.layers.value, layerId); if (!layer) { console.error(`图层 ${layerId} 不存在`); return; } const tempFabricObject = fabricObject?.toObject?.([ "id", "layerId", "layerName", "isBackgroud", "isFixed", ]) || fabricObject; if (layer.isFixed || layer.isBackground) { layer.fabricObject = tempFabricObject; } else { layer.fabricObjects = layer.fabricObjects.map((item) => { if (item.id === tempFabricObject.id) { // 更新对象属性 return tempFabricObject; } return item; }) || []; } } /** * 移动图层位置 * @param {string} layerId 图层ID * @param {string} direction 移动方向,'up'、'down'、'toTop'或'toBottom' * @returns {boolean} 是否移动成功 */ moveLayer(layerId, direction) { // 查找要移动的图层 const { layer } = findLayerRecursively(this.layers.value, layerId); // 如果是背景层或固定层,不允许移动 if (layer && (layer.isBackground || layer.isFixed)) { console.warn( layer.isBackground ? this.t("背景层不可移动") : this.t("固定层不可移动") ); return false; } let command; // 根据方向选择相应的命令 switch (direction) { case "toTop": command = new MoveLayerToTopCommand({ canvas: this.canvas, layers: this.layers, layerId: layerId, layerSort: this.layerSort, }); break; case "toBottom": command = new MoveLayerToBottomCommand({ canvas: this.canvas, layers: this.layers, layerId: layerId, layerSort: this.layerSort, }); break; case "up": case "down": default: command = new MoveLayerCommand({ canvas: this.canvas, layers: this.layers, layerId: layerId, layerSort: this.layerSort, direction: direction, }); break; } // 执行命令 if (this.commandManager) { const result = this.commandManager.execute(command); if (result) { console.log(`✅ 图层移动成功: ${direction}`); } return result; } else { const result = command.execute(); if (result) { // 更新画布渲染顺序 console.log(`✅ 图层移动成功: ${direction}`); } return result; } } // 设置激活当前图层下画布中的所有对象,并变成选择组 setAllActiveGroupLayerCanvasObject(layer) { // 获取当前图层下所有元素 // 选择当前组下所有画布元素 const allObjects = layer.children.reduce((acc, child) => { // 如果子图层有fabricObjects,则添加到结果数组 child?.fabricObjects?.forEach((obj) => { const { object } = findObjectById(this.canvas, obj.id); if (object) { // if (!layerMask) { // layerMask = fabric.util.object.clone(object.clipPath); // } // object.clipPath = null; // 确保克隆的遮罩没有clipPath acc.push(object); } }); if (child?.fabricObject) { const { object } = findObjectById(this.canvas, child?.fabricObject.id); if (object) { // if (!layerMask) { // layerMask = fabric.util.object.clone(object.clipPath); // } // object.clipPath = null; // 确保克隆的遮罩没有clipPath acc.push(object); } } // if (child.fill) { // // 如果图层有填充颜色,设置所有对象的填充颜色 // const { object } = findObjectById(this.canvas, child.fill.id); // if (object) { // acc.push(object); // } // } return acc; }, []); // if (layer.fill) { // // 如果图层有填充颜色,设置所有对象的填充颜色 // const { object } = findObjectById(this.canvas, layer.fill.id); // if (object) { // allObjects.push(object); // } // } if (allObjects.length) { // 切换到选择模式 this?.toolManager?.setTool(OperationType.SELECT); // 如果有对象,创建选择组 this.canvas.discardActiveObject(); // 取消当前活动对象 this.canvas.renderAll(); // 确保画布渲染 // const { object } = findObjectById(this.canvas, layer.clippingMask?.id); // 选中多个对象,不是创建组 // 多个对象时创建活动选择组 let activeSelection = new fabric.ActiveSelection(allObjects, { canvas: this.canvas, }); // if (object) { // const tempClipPath = fabric.util.object.clone(object); // tempClipPath.clipPath = null; // tempClipPath.set({ // // 设置绝对定位 // // ...layer.clippingMask, // 恢复原定位 // ...getOriginObjectInfo(layer.clippingMask), // absolutePositioned: true, // }); // activeSelection.clipPath = tempClipPath; // 保留第一个对象的裁剪路径 // } // // 监听选择取消事件,恢复原始裁剪路径 // const restoreClipPaths = () => { // allObjects.forEach((obj) => { // if (obj._originalClipPath !== undefined) { // obj.clipPath = obj._originalClipPath; // delete obj._originalClipPath; // } // }); // this.canvas.off("selection:cleared", restoreClipPaths); // this.canvas.off("selection:updated", restoreClipPaths); // }; // this.canvas.on("selection:cleared", restoreClipPaths); // this.canvas.on("selection:updated", restoreClipPaths); // 设置活动选择组的属性 this.canvas.setActiveObject(activeSelection); // 为活动选择组添加移动事件监听器,用于同步更新遮罩位置 this._setupGroupMaskMovementSync(activeSelection, layer); activeSelection = null; // 清理引用,避免内存泄漏 // 确保选择组正确渲染 // activeSelection.setCoords(); } } /** * 选择图层下的所有画布对象 * @param {string} layerId 图层ID * @returns {fabric.ActiveSelection|null} 返回活动选择组或null */ async selectLayerObjects(layerId) { // 查找图层 const { layer } = findLayerRecursively(this.layers.value, layerId); if (!layer) { console.error(`图层 ${layerId} 不存在`); return; } // 获取图层下的所有对象 const objects = layer.fabricObjects ?.map((obj) => { const { object } = findObjectById(this.canvas, obj.id); return object; }) ?.filter(Boolean) || []; if (objects.length === 0) { console.warn(`图层 ${layerId} 没有对象可供选择`); return; } // 切换到选择模式 this?.toolManager?.setTool(OperationType.SELECT); if(objects.length === 1) { this.canvas.setActiveObject(objects[0]); return objects[0]; } // 创建一个新的活动选择组 const activeSelection = new fabric.ActiveSelection(objects, { canvas: this.canvas, preserveObjectStacking: true, // 保留对象堆叠顺序 }); // 设置活动选择组的属性 this.canvas.setActiveObject(activeSelection); // 渲染画布 this.canvas.renderAll(); // 设置活动选择组的坐标 activeSelection.setCoords(); // 更新对象交互性 this.updateLayersObjectsInteractivity(); // 返回活动选择组 return activeSelection; } /** * 切换图层可见性 * @param {string} layerId 图层ID * @returns {boolean} 更新后的可见性状态 */ async toggleLayerVisibility(layerId) { // 直接创建和执行命令 const command = new ToggleLayerVisibilityCommand({ canvas: this.canvas, layers: this.layers, layerId: layerId, layerManager: this, }); // 执行命令 if (this.commandManager) { return await this.commandManager.execute(command); } else { return await command.execute(); } } /** * 查询图层可见性 * @param {string} layerId 图层ID * @returns {boolean} 图层是否可见 */ getLayerVisibility(layerId) { // 查找图层 const { layer } = findLayerRecursively(this.layers.value, layerId); if (!layer) { console.error(`图层 ${layerId} 不存在`); return false; } // 返回图层的可见性状态 return layer.visible; } /** * 切换图层锁定状态 * @param {string} layerId 图层ID * @returns {boolean} 更新后的锁定状态 */ toggleLayerLock(layerId) { // 直接创建和执行命令 const command = new LayerLockCommand({ canvas: this.canvas, layers: this.layers, layerId: layerId, layerManager: this, }); // 执行命令 if (this.commandManager) { this.commandManager.execute(command); } else { command.execute(); } // 更新对象交互性 this.updateLayersObjectsInteractivity(); // 获取当前锁定状态 const layer = this.layers.value.find((layer) => layer.id === layerId); return layer ? layer.locked : false; } /** * 设置图层不透明度 * @param {string} layerId 图层ID * @param {number} opacity 不透明度值 (0-1) */ setLayerOpacity(layerId, opacity) { // 直接创建和执行命令 const command = new SetLayerOpacityCommand({ canvas: this.canvas, layers: this.layers, layerId: layerId, opacity: opacity, }); // 执行命令 if (this.commandManager) { this.commandManager.execute(command); } else { command.execute(); } // 更新图层对象不透明度 const layer = this.layers.value.find((layer) => layer.id === layerId); if (layer && layer.fabricObjects) { layer.fabricObjects.forEach((obj) => { obj.opacity = opacity; }); this.canvas.renderAll(); } } /** * 设置图层混合模式 * @param {string} layerId 图层ID * @param {string} blendMode 混合模式 */ setLayerBlendMode(layerId, blendMode) { // 直接创建和执行命令 const command = new SetLayerBlendModeCommand({ canvas: this.canvas, layers: this.layers, layerId: layerId, blendMode: blendMode, }); // 执行命令 if (this.commandManager) { this.commandManager.execute(command); } else { command.execute(); } // 更新图层对象混合模式 const layer = this.layers.value.find((layer) => layer.id === layerId); if (layer && layer.fabricObjects) { layer.fabricObjects.forEach((obj) => { obj.globalCompositeOperation = blendMode; }); this.canvas.renderAll(); } } /** * 重命名图层 * @param {string} layerId 图层ID * @param {string} newName 新名称 */ renameLayer(layerId, newName) { // 直接创建和执行命令 const command = new RenameLayerCommand({ canvas: this.canvas, layers: this.layers, layerId: layerId, newName: newName, }); // 执行命令 if (this.commandManager) { this.commandManager.execute(command); } else { command.execute(); } // 更新图层对象上的图层名称 const layer = this.layers.value.find((layer) => layer.id === layerId); if (layer && layer.fabricObjects) { layer.fabricObjects.forEach((obj) => { obj.layerName = newName; }); } } /** * 合并多个图层 * @param {Array} layerIds 要合并的图层ID数组 * @param {string} newName 合并后的图层名称,可选 * @returns {string} 合并后的新图层ID */ mergeLayers(layerIds, newName = null) { // 检查参数 if (!layerIds || !Array.isArray(layerIds) || layerIds.length < 2) { console.error("合并图层至少需要两个图层ID"); return null; } // 直接创建和执行命令 const command = new MergeLayersCommand({ canvas: this.canvas, layers: this.layers, layerIds: layerIds, newName: newName, }); // 执行命令 if (this.commandManager) { this.commandManager.execute(command); } else { command.execute(); } return command.newLayerId; } /** * 将多个图层组合为一个组 * @param {Array} layerIds 要组合的图层ID数组 * @param {string} groupName 组名称,可选 * @returns {string} 新组图层ID */ async groupLayers(layerIds, groupName = null) { // 检查参数 if (!layerIds || !Array.isArray(layerIds) || layerIds.length < 2) { console.error("组合图层至少需要两个图层ID"); return null; } // 直接创建和执行命令 const command = new GroupLayersCommand({ canvas: this.canvas, layers: this.layers, activeLayerId: this.activeLayerId, layerIds: layerIds, groupName: groupName, }); // 执行命令 if (this.commandManager) { return await this.commandManager.execute(command); } else { return await command.execute(); } } /** * 解组一个组图层 * @param {string} groupId 要解组的组图层ID * @returns {Array} 解组后的图层ID数组 */ async ungroupLayers(groupId) { // 查找组图层 const groupLayer = this.layers.value.find((l) => l.id === groupId); if ( !groupLayer || !groupLayer.children || groupLayer.children.length === 0 ) { console.error(`${groupId} 不是有效的组图层或不包含子图层`); return null; } // 直接创建和执行命令 const command = new UngroupLayersCommand({ canvas: this.canvas, layers: this.layers, groupId: groupId, activeLayerId: this.activeLayerId, }); // 执行命令 if (this.commandManager) { return await this.commandManager.execute(command); } else { return await command.execute(); } } /** * 调整画布和背景图层尺寸 * @param {number} width 宽度 * @param {number} height 高度 */ resizeCanvas(width, height, options = {}) { // 直接创建和执行命令 const command = new BackgroundSizeCommand({ canvas: this.canvas, layers: this.layers, canvasManager: this.canvasManager, newWidth: width, newHeight: height, isRedGreenMode: this.isInRedGreenMode(), // 传递红绿图模式状态 }); command.undoable = options.undoable; // 设置为可撤销 // 执行命令 if (this.commandManager) { this.commandManager.execute(command); } else { command.execute(); } // 更新存储的尺寸 this.canvasWidth = width; this.canvasHeight = height; } /** * 调整背景大小并等比缩放所有其他元素 * @param {number} width 宽度 * @param {number} height 高度 */ resizeCanvasWithScale(width, height, options = {}) { // 检查是否有除背景层外的其他元素 const hasOtherElements = this.canvas .getObjects() .some((obj) => !obj.isBackground); if (hasOtherElements) { // 有其他元素时使用带缩放的命令 const command = new BackgroundSizeWithScaleCommand({ canvas: this.canvas, layers: this.layers, canvasManager: this.canvasManager, newWidth: width, newHeight: height, isRedGreenMode: this.isInRedGreenMode(), // 传递红绿图模式状态 }); command.undoable = false; // 设置为可撤销 // 执行命令 if (this.commandManager) { this.commandManager.execute(command); } else { command.execute(); } } else { // 没有其他元素时使用普通的背景尺寸调整命令 this.resizeCanvas(width, height, options); } // 更新存储的尺寸 this.canvasWidth = width; this.canvasHeight = height; } /** * 排序图层,确保图层顺序: 普通图层 > 固定图层 > 背景图层 */ sortLayers() { // 对图层进行排序:背景图层在最底层(数组最后),固定图层在中间 this.layers.value.sort((a, b) => { // 如果a是背景图层,它应该排在后面(最底层) if (a.isBackground) return 1; // 如果b是背景图层,它应该排在后面(最底层) if (b.isBackground) return -1; // 如果a是固定图层而b不是固定图层,a应该排在后面(固定图层在普通图层下方) if (a.isFixed && !b.isFixed) return 1; // 如果b是固定图层而a不是固定图层,b应该排在后面(固定图层在普通图层下方) if (b.isFixed && !a.isFixed) return -1; // 其他情况保持原有顺序 return 0; }); // 更新画布对象顺序 this._rearrangeObjects(); } /** * 重新排列画布上的对象以匹配图层顺序 * @private */ _rearrangeObjects() { if (this.layerSort) { // 使用LayerSort的高级排序 this.layerSort.rearrangeObjects(); return; } // 传统排序逻辑(保持原有逻辑作为备用) if (!this.canvas) return; // 获取画布上的所有对象 const canvasObjects = [ ...this.canvas.getObjects(["id", "layerId", "layerName"]), ]; // 清空画布 this.canvas.clear(); // 按图层顺序(从底到顶)重新添加对象 // 注意:图层数组是从顶到底的顺序,需要反向遍历 for (let i = this.layers.value.length - 1; i >= 0; i--) { const layer = this.layers.value[i]; // 跳过不可见图层 if (!layer.visible) continue; if (layer.isBackground && layer.fabricObject) { // 背景图层 const originalObj = canvasObjects.find( (o) => o.id === layer.fabricObject.id ); if (originalObj) { this.canvas.add(originalObj); } else { this.canvas.add(layer.fabricObject); } } else if ( layer.isFixed && layer.fabricObjects && layer.fabricObjects.length > 0 ) { // 固定图层 layer.fabricObjects.forEach((obj) => { const originalObj = canvasObjects.find((o) => o.id === obj.id); if (originalObj) { this.canvas.add(originalObj); } else { this.canvas.add(obj); } }); } else if ( Array.isArray(layer.fabricObjects) && layer.fabricObjects.length > 0 ) { // 普通图层,添加所有fabricObjects layer.fabricObjects.forEach((obj) => { const originalObj = canvasObjects.find((o) => o.id === obj.id); if (originalObj) { this.canvas.add(originalObj); } else { this.canvas.add(obj); } }); } } } /** * 同步画布对象到图层数据 * 确保画布上的对象和图层数据一致 */ syncCanvasToLayers() { if (!this.canvas) return; // 获取画布上的所有对象 const canvasObjects = this.canvas.getObjects(); // 遍历所有图层 this.layers.value.forEach((layer) => { if (layer.isBackground) { // 背景图层处理 if (layer.fabricObject) { const existsOnCanvas = canvasObjects.some( (obj) => obj.id === layer.fabricObject.id ); if (!existsOnCanvas) { this.canvas.add(layer.fabricObject); } } } else if (Array.isArray(layer.fabricObjects)) { // 更新图层中的对象列表 const updatedObjects = []; // 处理已有对象 layer.fabricObjects.forEach((obj) => { const canvasObj = canvasObjects.find((cObj) => cObj.id === obj.id); if (canvasObj) { updatedObjects.push(canvasObj); } else { // 对象不在画布上,添加到画布 this.canvas.add(obj); updatedObjects.push(obj); } }); // 检查是否有新对象需要添加到图层 canvasObjects.forEach((canvasObj) => { if ( canvasObj.layerId === layer.id && !updatedObjects.some((obj) => obj.id === canvasObj.id) ) { updatedObjects.push(canvasObj); } }); // 更新图层的对象列表 layer.fabricObjects = updatedObjects; } }); // 重新排列对象以匹配图层顺序 this._rearrangeObjects(); } /** * 导出当前图层数据 * @returns {Object} 图层数据对象 */ exportLayersData() { // 深拷贝图层数据,避免修改原始数据 const layersData = JSON.parse(JSON.stringify(this.layers.value)); // 移除无法序列化的属性 layersData.forEach((layer) => { if (layer.fabricObjects) { // 序列化对象 layer.serializedObjects = layer.fabricObjects .map((obj) => { if (typeof obj.toObject === "function") { return obj.toObject(["id", "layerId", "layerName"]); } return null; }) .filter(Boolean); // 删除原始对象引用 delete layer.fabricObjects; } if (layer.fabricObject) { layer.serializedBackgroundObject = typeof layer.fabricObject.toObject === "function" ? layer.fabricObject.toObject(["id", "layerId", "layerName"]) : null; delete layer.fabricObject; } }); return { layers: layersData, activeLayerId: this.activeLayerId.value, canvasWidth: this.canvasWidth, canvasHeight: this.canvasHeight, backgroundColor: this.backgroundColor.value, editorMode: this.editorMode, }; } /** * 获取当前图层序列化对象 * @param {Object} layer 图层对象 * @returns {Array} 图层中的序列化对象列表 */ getCurrLayerSerializedObjects(layer) { if (!layer || layer?.fabricObjects?.length === 0) { return []; } // 序列化图层中的对象 return layer.fabricObjects .map((obj) => { const { object } = findObjectById(this.canvas, obj.id); if (object) return object.toObject(["id", "layerId", "layerName"]); return false; }) .filter(Boolean); } /** * 复制图层数据到剪贴板 * @param {string} layerId 要复制的图层ID * @returns {Object} 复制的图层数据 */ copyLayer(layerId) { const { layer } = findLayerRecursively(this.layers.value, layerId); if (!layer) { console.error(`图层 ${layerId} 不存在`); return null; } // 不允许复制背景图层 if (layer.isBackground) { console.warn("不能复制背景图层"); return null; } // 序列化图层对象 const layerCopy = JSON.parse(JSON.stringify(layer)); // 序列化fabricObjects数组 if (layer.fabricObjects && layer.fabricObjects.length > 0) { layerCopy.serializedObjects = this.getCurrLayerSerializedObjects(layer); } // 处理子图层 if (layer?.children?.length) { layerCopy.children = layer.children.map((child) => { const childCopy = JSON.parse(JSON.stringify(child)); if (child.fabricObjects && child.fabricObjects.length > 0) { childCopy.serializedObjects = this.getCurrLayerSerializedObjects(child); } return childCopy; }); } // 存储到剪贴板 this.clipboardData = layerCopy; const input = document.createElement("input"); input.value = "aida_copy_canvas_layer: " + layer.name; document.body.appendChild(input); input.select(); document.execCommand("copy"); document.body.removeChild(input); console.log(`已复制图层:${layer.name}`); return this.clipboardData; } /** * 剪切图层数据到剪贴板 * @param {string} layerId 要剪切的图层ID * @returns {Object} 剪切的图层数据 */ cutLayer(layerId) { const layer = this.layers.value.find((l) => l.id === layerId); if (!layer) { console.error(`图层 ${layerId} 不存在`); return null; } // 不允许剪切背景图层 if (layer.isBackground) { console.warn("不能剪切背景图层"); return null; } // 检查是否是唯一的普通图层 const normalLayers = this.layers.value.filter((l) => !l.isBackground && !l.isFixed); console.log("普通图层:", normalLayers) if (normalLayers.length === 1) { console.warn("不能剪切唯一的普通图层"); return null; } // 序列化图层对象 const layerCopy = JSON.parse(JSON.stringify(layer)); // 序列化fabricObjects数组 if (layer.fabricObjects && layer.fabricObjects.length > 0) { layerCopy.serializedObjects = layer.fabricObjects .map((obj) => typeof obj.toObject === "function" ? obj.toObject(["id", "layerId", "layerName"]) : null ) .filter(Boolean); } // 存储到剪贴板 this.clipboardData = layerCopy; // 记录是剪切操作,用于粘贴时的处理 this.clipboardData.isCut = true; // 从画布中移除图层中的所有对象 if (layer.fabricObjects && layer.fabricObjects.length > 0) { layer.fabricObjects.forEach((obj) => { this.canvas.remove(obj); }); } // 如果剪切的是当前活动图层,需要切换到其他图层 if (this.activeLayerId.value === layerId) { // 查找下一个可用的图层 const nextLayer = this._findNextAvailableLayer(layerId); if (nextLayer) { this.setActiveLayer(nextLayer.id); } } // 直接创建和执行命令 const command = new RemoveLayerCommand({ canvas: this.canvas, layers: this.layers, layerId: layerId, activeLayerId: this.activeLayerId, }); // 执行命令 if (this.commandManager) { this.commandManager.execute(command); } else { command.execute(); } // 更新对象交互性 this.updateLayersObjectsInteractivity(); console.log(`已剪切图层:${layer.name}`); return this.clipboardData; } /** * 粘贴图层 * @returns {string} 新创建的图层ID */ /** * 粘贴图层 * @returns {string} 新创建的图层ID */ async pasteLayer(event) { console.log("剪贴板数据:", this.clipboardData,event); if (!this.clipboardData) { console.error("剪贴板中没有图层数据"); return null; } // 创建粘贴图层命令 const command = new PasteLayerCommand({ canvas: this.canvas, layers: this.layers, activeLayerId: this.activeLayerId, clipboardData: this.clipboardData, layerManager: this, // 传递LayerManager实例 }); // 设置命令为可撤销 command.undoable = true; if (this.commandManager) { // 使用命令管理器执行命令 const result = await this.commandManager.execute(command); this.clipboardData = null; // 清空剪贴板数据 复制一次 // 执行命令 return result; } const result = await command.execute(); this.clipboardData = null; // 清空剪贴板数据 复制一次就清空,避免重复粘贴 出现 错误后也清空 总之就是清空 不用给自己找麻烦 // 执行命令 return result; } /** * 选择所有图层 */ selectAll() { // 直接创建和执行命令 const command = new SelectAllLayersCommand({ canvas: this.canvas, layers: this.layers, layerManager: this, activeLayerId: this.activeLayerId, editorMode: this.editorMode, }); command.undoable = false; // 设置为不可撤销 // 执行命令 command?.execute?.(); } /** * 清除当前选择 */ clearSelection() { // 直接创建和执行命令 const command = new ClearSelectionCommand({ canvas: this.canvas, layers: this.layers, layerManager: this, activeLayerId: this.activeLayerId, editorMode: this.editorMode, }); command.undoable = false; // 设置为不可撤销 // 执行命令 command?.execute?.(); } /** * 查找下一个可用的图层(非背景、非锁定) * @param {string} excludeLayerId 要排除的图层ID * @returns {Object|null} 下一个可用的图层 * @private */ _findNextAvailableLayer(excludeLayerId) { // 查找第一个非背景、非锁定的图层,排除指定的图层 return ( this.layers.value.find( (layer) => layer.id !== excludeLayerId && !layer.isBackground && !layer.locked ) || null ); } /** * 获取活动图层上方的插入索引 * @returns {number} 插入索引 * @private */ _getInsertIndexAboveActiveLayer() { if (!this.activeLayerId.value) return 0; const activeLayerIndex = this.layers.value.findIndex( (layer) => layer.id === this.activeLayerId.value ); return activeLayerIndex !== -1 ? activeLayerIndex : 0; } /** * 保存画布状态 * 现在统一通过命令管理器的智能状态保存 */ saveCanvasState() { if (this.commandManager) { // 使用智能状态保存,避免不必要的状态保存 this.commandManager.saveStateIfNeeded({ name: "图层状态更新", stateType: "full", }); } } /** * 执行带状态保存的操作 * 统一的状态管理入口 */ executeWithState(operation, name = "图层操作") { if (this.commandManager) { return this.commandManager.executeWithState(operation, { name, stateType: "full", }); } else { // 降级处理 return typeof operation === "function" ? operation() : operation; } } /** * 批量执行图层操作 */ batchExecute(operations, name = "批量图层操作") { if (this.commandManager) { return this.commandManager.batch(operations, name); } else { // 降级处理 const results = []; for (const operation of operations) { results.push(typeof operation === "function" ? operation() : operation); } return results; } } /** * 清理画布,移除所有图层 */ clearCanvas() { // 清空画布 this.canvas.clear(); // 清空图层列表 this.layers.value = []; // 重新初始化基本图层 this.initializeLayers(); console.log("已清空画布"); } /** * 合并图层内的对象为单一图像 * @param {string} layerId 图层ID,默认使用当前活动图层 * @param {fabric.Image} newImage 要合并的新图像(可选) * @returns {Promise} 合并后的图像ID */ async mergeLayerObjects(activeLayer, fabricImage = null) { if (!activeLayer) { console.error(`活动图层不存在`); return null; } // 直接创建和执行命令 const command = new MergeLayerObjectsCommand({ canvas: this.canvas, layers: this.layers, fabricImage, activeLayer, }); // 执行命令 return command; } /** * 合并图层内对象成组的命令 * 将新的图像与图层内现有对象合并为一个组对象 * 注意:此命令与 MergeLayerObjectsCommand 类似,但它创建一个组而不是单个图像对象 */ async LayerObjectsToGroup(activeLayer, fabricImage = null) { // 检查活动图层是否存在 if (!activeLayer) { console.error(`活动图层不存在`); return null; } // 直接创建和执行命令 const command = new LayerObjectsToGroupCommand({ canvas: this.canvas, layers: this.layers, layerManager: this, fabricImage, activeLayer, }); // 执行命令 return command; } /** * 更新背景图层颜色 * @param {string} backgroundColor 背景颜色 */ updateBackgroundColor(backgroundColor, options = {}) { const backgroundLayer = this.layers.value.find( (layer) => layer.isBackground ); if (!backgroundLayer) { console.warn("没有找到背景图层"); return; } // 直接创建和执行命令 const command = new UpdateBackgroundCommand({ canvas: this.canvas, layers: this.layers, canvasManager: this.canvasManager, layerManger: this, backgroundColor, backgroundColorValue: this.backgroundColor, oldColor: options.oldColor, }); command.undoable = isBoolean(options.undoable) ? options.undoable : true; // 设置为可撤销 // 执行命令 if (this.commandManager) { this.commandManager.execute(command); } else { command.execute(); } } /** * 获取图层缩略图 * @param {string} layerId 图层ID * @param {number} width 缩略图宽度 * @param {number} height 缩略图高度 * @returns {string} Base64编码的缩略图 */ getLayerThumbnail(layerId, width = 100, height = 100) { const layer = this.getLayerById(layerId); if (!layer) { console.error(`图层 ${layerId} 不存在`); return null; } // 创建临时画布 const tempCanvas = document.createElement("canvas"); tempCanvas.width = width; tempCanvas.height = height; const ctx = tempCanvas.getContext("2d"); // 设置背景色 ctx.fillStyle = "#ffffff"; ctx.fillRect(0, 0, width, height); try { if (layer.isBackground && layer.fabricObject) { // 背景图层 const bgObj = layer.fabricObject; if (bgObj.toCanvasElement) { const element = bgObj.toCanvasElement(); ctx.drawImage(element, 0, 0, width, height); } } else if (layer.fabricObjects && layer.fabricObjects.length > 0) { // 普通图层 layer.fabricObjects.forEach((obj) => { if (obj.toCanvasElement) { const element = obj.toCanvasElement(); const scaleX = width / this.canvasWidth; const scaleY = height / this.canvasHeight; ctx.save(); ctx.scale(scaleX, scaleY); ctx.drawImage(element, obj.left || 0, obj.top || 0); ctx.restore(); } }); } return tempCanvas.toDataURL(); } catch (error) { console.error("生成图层缩略图失败:", error); return null; } } /** * 检查图层是否为空 * @param {string} layerId 图层ID * @returns {boolean} 是否为空图层 */ isLayerEmpty(layerId) { const layer = this.getLayerById(layerId); if (!layer) return true; if (layer.isBackground) { return !layer.fabricObject; } return !layer.fabricObjects || layer.fabricObjects.length === 0; } /** * 获取图层统计信息 * @returns {Object} 图层统计信息 */ getLayerStats() { const stats = { totalLayers: this.layers.value.length, backgroundLayers: 0, normalLayers: 0, lockedLayers: 0, hiddenLayers: 0, emptyLayers: 0, totalObjects: 0, }; this.layers.value.forEach((layer) => { if (layer.isBackground) { stats.backgroundLayers++; if (layer.fabricObject) { stats.totalObjects++; } } else { stats.normalLayers++; if (layer.fabricObjects) { stats.totalObjects += layer.fabricObjects.length; } } if (layer.locked) stats.lockedLayers++; if (!layer.visible) stats.hiddenLayers++; if (this.isLayerEmpty(layer.id)) stats.emptyLayers++; }); return stats; } /** * 清理空图层 * @returns {Array} 被清理的图层ID数组 */ cleanupEmptyLayers() { const emptyLayerIds = []; // 找出所有空的非背景图层 this.layers.value.forEach((layer) => { if (!layer.isBackground && this.isLayerEmpty(layer.id)) { emptyLayerIds.push(layer.id); } }); // 删除空图层 emptyLayerIds.forEach((layerId) => { this.removeLayer(layerId); }); if (emptyLayerIds.length > 0) { console.log(`已清理 ${emptyLayerIds.length} 个空图层`); } return emptyLayerIds; } /** * 销毁图层管理器 * 清理所有引用和事件监听器 */ dispose() { // 清空画布 if (this.canvas) { this.canvas.clear(); } // 清空图层数据 if (this.layers && this.layers.value) { this.layers.value = []; } // 清空剪贴板 this.clipboardData = null; // 清除引用 this.canvas = null; this.layers = null; this.activeLayerId = null; this.commandManager = null; this.canvasManager = null; this.toolManager = null; console.log("LayerManager 已销毁"); } /** * 创建文本图层并添加文本对象 * @param {Object} textObject Fabric文本对象 * @param {Object} options 文本选项 * @returns {Object} 创建的文本对象 */ async createTextLayerWithObject(textObject, options = {}) { if (!this.canvas || !textObject) return null; // 确保对象有ID textObject.id = textObject.id || `text_${Date.now()}_${Math.floor(Math.random() * 1000)}`; // 创建文本图层 const layerName = options.name || "文本图层"; const layerId = await this.createLayer(layerName, LayerType.TEXT, { layerProperties: { text: options.text || textObject.text || "新文本", fontFamily: options.fontFamily || textObject.fontFamily || "Arial", fontSize: options.fontSize || textObject.fontSize || 24, fontWeight: options.fontWeight || textObject.fontWeight || "normal", fontStyle: options.fontStyle || textObject.fontStyle || "normal", textAlign: options.textAlign || textObject.textAlign || "left", underline: options.underline || textObject.underline || false, linethrough: options.linethrough || textObject.linethrough || false, overline: options.overline || textObject.overline || false, fill: options.fill || textObject.fill || "#000000", textBackgroundColor: options.textBackgroundColor || textObject.textBackgroundColor || "transparent", lineHeight: options.lineHeight || textObject.lineHeight || 1.16, charSpacing: options.charSpacing || textObject.charSpacing || 0, }, }); // 把对象添加到新图层 textObject.layerId = layerId; textObject.layerName = layerName; // 添加到画布,如果还未添加 const isOnCanvas = this.canvas .getObjects() .some((obj) => obj.id === textObject.id); if (!isOnCanvas) { this.canvas.add(textObject); } // 更新图层中的对象列表 const layer = this.getLayerById(layerId); if (layer) { layer.fabricObjects = layer.fabricObjects || []; layer.fabricObjects.push( textObject.toObject(["id", "layerId", "layerName"]) ); } // 设置此图层为活动图层 this.setActiveLayer(layerId); // 更新交互性 this.updateLayersObjectsInteractivity(); return textObject; } /** * 根据fabric对象查找所属图层 * @param {Object} fabricObject fabric对象 * @returns {Object|null} 图层对象或null */ findLayerByObject(fabricObject) { if (!fabricObject || !fabricObject.id) { return null; } // 遍历所有图层查找包含该对象的图层 for (const layer of this.layers.value) { // 检查背景图层 if (layer.isBackground && layer.fabricObject) { if (layer.fabricObject.id === fabricObject.id) { return layer; } } // 检查普通图层 if (layer.fabricObjects && Array.isArray(layer.fabricObjects)) { const foundObject = layer.fabricObjects.find( (obj) => obj.id === fabricObject.id ); if (foundObject) { return layer; } } } return null; } /** * 拖拽排序图层 * @param {number} oldIndex 原索引 * @param {number} newIndex 新索引 * @param {string} layerId 图层ID * @returns {boolean} 是否排序成功 */ async reorderLayers(oldIndex, newIndex, layerId) { // 检查索引有效性 if ( oldIndex < 0 || newIndex < 0 || oldIndex >= this.layers.value.length || newIndex >= this.layers.value.length ) { console.warn("图层排序索引无效"); return false; } // 检查是否是同一位置 if (oldIndex === newIndex) { return true; } // 获取要移动的图层 const layer = this.layers.value[oldIndex]; if (!layer || layer.id !== layerId) { console.warn("图层ID与索引不匹配"); return false; } // 检查是否是背景层或固定层(不允许排序) if (layer.isBackground || layer.isFixed) { console.warn("背景层和固定层不能参与排序"); return false; } // 检查目标位置是否合法(不能移到背景层或固定层的位置) const targetLayer = this.layers.value[newIndex]; if (targetLayer && (targetLayer.isBackground || targetLayer.isFixed)) { console.warn("不能移动到背景层或固定层的位置"); return false; } // 创建并执行拖拽排序命令 const command = new ReorderLayersCommand({ layers: this.layers, oldIndex: oldIndex, newIndex: newIndex, layerId: layerId, canvas: this.canvas, layerSort: this.layerSort, }); // 执行命令 if (this.commandManager) { return await this.commandManager.execute(command); } else { return await command.execute(); } } /** * 拖拽排序子图层 * @param {string} parentId 父图层ID * @param {number} oldIndex 原索引 * @param {number} newIndex 新索引 * @param {string} layerId 子图层ID * @returns {boolean} 是否排序成功 */ reorderChildLayers(parentId, oldIndex, newIndex, layerId) { return this.layerSort?.reorderChildLayers( parentId, oldIndex, newIndex, layerId ); } /** * 根据图层ID和父图层ID查找子图层 * @param {string} layerId 子图层ID * @param {string} parentId 父图层ID * @returns {Object|null} 子图层对象或null */ findChildLayer(layerId, parentId) { const parentLayer = this.getLayerById(parentId); if (!parentLayer || !parentLayer.children) return null; return parentLayer.children.find((child) => child.id === layerId) || null; } /** * 删除子图层 * @param {string} layerId 子图层ID * @param {string} parentId 父图层ID * @returns {boolean} 是否删除成功 */ async removeChildLayer(layerId, parentId) { // 直接创建和执行命令 const command = new RemoveChildLayerCommand({ canvas: this.canvas, layers: this.layers, layerId: layerId, parentId: parentId, activeLayerId: this.activeLayerId, layerManager: this, }); // 执行命令 if (this.commandManager) { return await this.commandManager.execute(command); } else { return await command.execute(); } } /** * 重命名子图层 * @param {string} layerId 子图层ID * @param {string} parentId 父图层ID * @param {string} newName 新名称 */ renameChildLayer(layerId, parentId, newName) { // 直接创建和执行命令 const command = new RenameChildLayerCommand({ canvas: this.canvas, layers: this.layers, layerId: layerId, parentId: parentId, newName: newName, }); // 执行命令 if (this.commandManager) { this.commandManager.execute(command); } else { command.execute(); } } /** * 切换子图层锁定状态 * @param {string} layerId 子图层ID * @param {string} parentId 父图层ID * @returns {boolean} 更新后的锁定状态 */ toggleChildLayerLock(layerId, parentId) { // 直接创建和执行命令 const command = new ChildLayerLockCommand({ canvas: this.canvas, layers: this.layers, layerId: layerId, parentId: parentId, layerManager: this, }); // 执行命令 if (this.commandManager) { this.commandManager.execute(command); } else { command.execute(); } // 更新对象交互性 this.updateLayersObjectsInteractivity(); // 获取当前锁定状态 const childLayer = this.findChildLayer(layerId, parentId); return childLayer ? childLayer.locked : false; } /** * 切换子图层可见性 * @param {string} layerId 子图层ID * @param {string} parentId 父图层ID * @returns {boolean} 更新后的可见性状态 */ async toggleChildLayerVisibility(layerId, parentId) { // 直接创建和执行命令 const command = new ToggleChildLayerVisibilityCommand({ canvas: this.canvas, layers: this.layers, layerId: layerId, parentId: parentId, layerManager: this, }); // 执行命令 if (this.commandManager) { return await this.commandManager.execute(command); } else { return await command.execute(); } } // ==================== 红绿图模式相关操作 ==================== // 设置红绿图模式管理器 setRedGreenModeManager(redGreenModeManager) { this.redGreenModeManager = redGreenModeManager; } // 启用红绿图模式 enableRedGreenMode() { this.isRedGreenMode = true; console.log("图层管理器:红绿图模式已启用"); } // 禁用红绿图模式 disableRedGreenMode() { this.isRedGreenMode = false; console.log("图层管理器:红绿图模式已禁用"); } // 检查是否为红绿图模式 isInRedGreenMode() { return this.isRedGreenMode; } // 检查背景图层是否需要跟随画布大小变化(红绿图模式下) shouldBackgroundFollowCanvasSize() { return this.isRedGreenMode; } // 在红绿图模式下创建图层 - 限制功能 createLayerInRedGreenMode() { console.warn("红绿图模式下不支持创建新图层"); return null; } // 在红绿图模式下移除图层 - 限制功能 removeLayerInRedGreenMode(layerId) { console.warn("红绿图模式下不支持删除图层"); return false; } // ==================== 高级图层排序功能 ==================== /** * 使用LayerSort工具重新排列画布对象 * 这是页面操作排序图层后重建画布顺序的核心方法 * @param {boolean} async 是否使用异步处理,默认根据对象数量自动决定 * @returns {Promise|void} 如果使用异步则返回Promise */ rearrangeCanvasObjects(async = null) { if (!this.layerSort) { console.warn("图层排序工具未初始化,使用传统排序方法"); this._rearrangeObjects(); return; } // 检查是否需要异步处理 const objectsCount = this.canvas?.getObjects()?.length || 0; const layersCount = this.layers?.value?.length || 0; const shouldUseAsync = async !== null ? async : LayerSortUtils.shouldUseAsyncProcessing(objectsCount, layersCount); if (shouldUseAsync) { console.log( `使用异步排序处理 ${objectsCount} 个对象, ${layersCount} 个图层` ); return this.layerSort.rearrangeObjectsAsync(); } else { console.log( `使用同步排序处理 ${objectsCount} 个对象, ${layersCount} 个图层` ); this.layerSort.rearrangeObjects(); } } /** * 智能排序图层 * 根据对象类型和位置自动调整图层顺序 * @param {Array} targetLayerIds 要排序的图层ID数组,null表示排序所有普通图层 * @returns {boolean} 是否排序成功 */ smartSortLayers(targetLayerIds = null) { if (!this.layerSort) { console.warn("图层排序工具未初始化"); return false; } const result = this.layerSort.smartSort(targetLayerIds); if (result) { console.log("智能排序完成"); // 更新对象交互性 this.updateLayersObjectsInteractivity(); } return result; } /** * 优化图层结构 * 清理空图层、合并相邻相似图层等 * @returns {Object} 优化结果统计 */ optimizeLayerStructure() { if (!this.layerSort) { console.warn("图层排序工具未初始化,使用基础清理方法"); return { removedEmptyLayers: this.cleanupEmptyLayers().length, mergedLayers: 0, reorderedLayers: 0, }; } const stats = this.layerSort.optimizeLayerStructure(); if (stats.removedEmptyLayers > 0 || stats.reorderedLayers > 0) { console.log( `图层结构优化完成: 清理空图层 ${stats.removedEmptyLayers} 个, 重新排序 ${stats.reorderedLayers} 个图层` ); // 更新对象交互性 this.updateLayersObjectsInteractivity(); } return stats; } /** * 验证画布对象顺序是否正确 * @returns {boolean} 顺序是否正确 */ validateObjectOrder() { if (!this.layerSort) { console.warn("图层排序工具未初始化"); return true; // 无法验证,假设正确 } const isValid = this.layerSort.validateObjectOrder(); if (!isValid) { console.warn("画布对象顺序不正确,建议重新排列"); } return isValid; } canMoveToTop(layerId) { // 检查图层所在索引是否为0 const layerIndex = this.getLayerIndex(layerId); if (layerIndex === -1) { console.warn(`图层 ${layerId} 不存在`); return false; } return layerIndex > 0; // 如果索引大于0,则可以移动到顶部 } canMoveToBottom(layerId) { // 检查图层所在索引是否为0 const layerIndex = this.getLayerIndex(layerId); if (layerIndex === -1) { console.warn(`图层 ${layerId} 不存在`); return false; } const { parent } = findLayerRecursively(this.layers.value, layerId); if (parent?.children.length) { // 如果是子图层,检查是否有兄弟图层 return layerIndex < parent.children.length - 1; // 如果不是最后一个子图层,则可以移动到底部 } return layerIndex < this.layers?.value?.length - 3; // 如果索引小于长度 - 3,则可以移动到底部 } /** * 移动图层到指定位置 * 使用LayerSort的高级移动功能 * @param {string} layerId 要移动的图层ID * @param {number} newIndex 新位置索引 * @returns {boolean} 是否移动成功 */ moveLayerToIndex({ parentId, oldIndex, newIndex, layerId }) { if (!this.layerSort) { console.warn("图层排序工具未初始化,使用基础移动方法"); return this.moveLayer( layerId, newIndex > this.getLayerIndex(layerId) ? "down" : "up" ); } const result = this.layerSort.moveLayerToIndex({ parentId, oldIndex, newIndex, layerId, }); if (result) { console.log( `图层 ${layerId} - oldIndex: ${oldIndex} 已移动到位置 ${newIndex}` ); // 更新对象交互性 // this.updateLayersObjectsInteractivity(); } return result; } /** * 获取图层的有效移动范围 * @param {string} layerId 图层ID * @returns {Object} 包含最小和最大索引的对象 */ getLayerMoveRange(layerId) { if (!this.layerSort) { // 基础实现:只能在普通图层范围内移动 const normalLayers = this.layers.value .map((layer, index) => ({ layer, index })) .filter((item) => !item.layer.isBackground && !item.layer.isFixed); if (normalLayers.length === 0) { return { minIndex: -1, maxIndex: -1 }; } return { minIndex: normalLayers[0].index, maxIndex: normalLayers[normalLayers.length - 1].index, }; } return this.layerSort.getLayerMoveRange(layerId); } /** * 获取图层在数组中的索引 * @param {string} layerId 图层ID * @returns {number} 图层索引,-1表示未找到 */ getLayerIndex(layerId) { if (!layerId) { console.warn("未提供图层ID"); return -1; } let layerIndex = this.layers.value.findIndex( (layer) => layer.id === layerId ); if (layerIndex >= 0) return layerIndex; // 如果未找到,尝试在子图层中查找 const { parent } = findLayerRecursively(this.layers.value, layerId); parent?.children?.findIndex((child) => { if (child.id === layerId) { layerIndex = parent.children.indexOf(child); return true; // 找到后停止查找 } return false; }); return layerIndex; } /** * 高级拖拽排序图层 * 使用LayerSort的专业排序功能 * @param {number} oldIndex 原索引 * @param {number} newIndex 新索引 * @param {string} layerId 图层ID * @returns {boolean} 是否排序成功 */ advancedReorderLayers(oldIndex, newIndex, layerId) { if (!this.layerSort) { console.warn("图层排序工具未初始化,使用基础排序方法"); return this.reorderLayers(oldIndex, newIndex, layerId); } const result = this.layerSort.reorderLayers(oldIndex, newIndex, layerId); if (result) { console.log( `高级排序完成: 图层 ${layerId} 从位置 ${oldIndex} 移动到 ${newIndex}` ); // 更新对象交互性 this.updateLayersObjectsInteractivity(); } return result; } /** * 高级拖拽排序子图层 * @param {string} parentId 父图层ID * @param {number} oldIndex 原索引 * @param {number} newIndex 新索引 * @param {string} layerId 子图层ID * @returns {boolean} 是否排序成功 */ advancedReorderChildLayers(parentId, oldIndex, newIndex, layerId) { const result = this.reorderChildLayers( parentId, oldIndex, newIndex, layerId ); if (result) { console.log( `高级子图层排序完成: 图层 ${layerId} 在父图层 ${parentId} 中从位置 ${oldIndex} 移动到 ${newIndex}` ); // 重新排列画布对象 this.rearrangeCanvasObjects(); } return result; } /** * 使用LayerSort工具进行图层排序 * 这会同时更新图层数组和画布对象顺序 */ sortLayersWithTool() { if (!this.layerSort) { console.warn("图层排序工具未初始化,使用基础排序方法"); this.sortLayers(); return; } // 使用LayerSort工具排序 this.layers.value = this.layerSort.sortLayers(); // 重新排列画布对象 this.rearrangeCanvasObjects(); console.log("使用LayerSort工具完成图层排序"); } /** * 强制重建画布对象顺序 * 当图层顺序发生变化后调用此方法确保画布对象顺序正确 */ forceRebuildCanvasOrder() { console.log("强制重建画布对象顺序"); if (this.layerSort) { // 使用LayerSort的高级排序 this.rearrangeCanvasObjects(); } else { // 使用传统方法 this._rearrangeObjects(); } // 验证排序结果 const isValid = this.validateObjectOrder(); if (!isValid) { console.warn("画布对象顺序验证失败,可能需要手动调整"); } // 更新对象交互性 this.updateLayersObjectsInteractivity(); } /** * 批量重新排序多个图层 * @param {Array} reorderOperations 排序操作数组 [{layerId, oldIndex, newIndex}] * @returns {boolean} 是否全部操作成功 */ batchReorderLayers(reorderOperations) { if (!reorderOperations || !Array.isArray(reorderOperations)) { console.error("无效的批量排序操作"); return false; } console.log(`开始批量重新排序 ${reorderOperations.length} 个图层`); let allSuccessful = true; // 暂停渲染以提高性能 const wasRenderOnAddRemove = this.canvas.renderOnAddRemove; this.canvas.renderOnAddRemove = false; try { // 按照操作顺序执行 for (const operation of reorderOperations) { const { layerId, oldIndex, newIndex } = operation; const success = this.layerSort ? this.layerSort.reorderLayers(oldIndex, newIndex, layerId) : this.reorderLayers(oldIndex, newIndex, layerId); if (!success) { console.warn(`图层 ${layerId} 排序失败`); allSuccessful = false; } } // 重新排列画布对象 this.rearrangeCanvasObjects(); } finally { // 恢复渲染 this.canvas.renderOnAddRemove = wasRenderOnAddRemove; this.canvas.renderAll(); } if (allSuccessful) { console.log("批量图层排序完成"); } else { console.warn("批量图层排序部分失败"); } return allSuccessful; } /** * 合并组图层 * @param {string} groupId 组图层ID * @returns {Array} 所有子图层成组的图层ID */ async mergeGroupLayers(groupId) { // 查找组图层 const groupLayer = this.layers.value.find((l) => l.id === groupId); if ( !groupLayer || !groupLayer.children || groupLayer.children.length === 0 ) { console.warn(this.t("找不到有效的组图层或组图层为空")); return []; } // 直接创建和执行解组命令 const command = new MergeGroupLayerCommand({ canvas: this.canvas, layers: this.layers, layerId: groupId, activeLayerId: this.activeLayerId, layerManager: this, }); // 执行命令 if (this.commandManager) { const result = await this.commandManager.execute(command); result && console.log(`✅ 成功合并组图层: ${groupLayer.name}`); return result; } else { const result = await command.execute(); result && console.log(`✅ 成功合并组图层: ${groupLayer.name}`); return result || []; } } /** * 栅格化图层 * @param {string} layerId 图层ID,默认使用当前活动图层 * @returns {Promise} 是否栅格化成功 */ async rasterizeLayer(layerId = null) { const targetLayerId = layerId || this.activeLayerId.value; if (!targetLayerId) { console.warn(this.t("没有指定要栅格化的图层")); return false; } // 查找目标图层 // const targetLayer = this.getLayerById(targetLayerId); const { layer: targetLayer } = findLayerRecursively( this.layers.value, targetLayerId ); if (!targetLayer) { console.error(this.t("图层不存在", { layerId: targetLayerId })); return false; } // 不允许栅格化背景图层和固定图层 if (targetLayer.isBackground || targetLayer.isFixed) { console.warn(this.t("背景图层和固定图层不能栅格化")); return false; } // 直接创建和执行栅格化命令 const command = new RasterizeLayerCommand({ canvas: this.canvas, layers: this.layers, layerId: targetLayerId, activeLayerId: this.activeLayerId, layerManager: this, }); // 执行命令 if (this.commandManager) { const result = await this.commandManager.execute(command); if (result) { console.log(`✅ 成功栅格化图层: ${targetLayer.name}`); } return result; } else { const result = await command.execute(); if (result) { console.log(`✅ 成功栅格化图层: ${targetLayer.name}`); } return result; } } /** * 检查图层是否可以栅格化 * @param {string} layerId 图层ID * @returns {boolean} 是否可以栅格化 */ canRasterizeLayer(layerId) { const layer = findLayerRecursively(this.layers.value, layerId)?.layer; if (!layer) return false; // 不允许栅格化背景图层和固定图层 if (layer.isBackground || layer.isFixed) { return false; } // 检查图层是否有内容可以栅格化 if (layer.type === "group" || layer.children.length > 0) { // 组图层:检查是否有子图层且子图层有内容 return layer.children.some( (child) => child.fabricObjects && child.fabricObjects.length > 0 ); } else { // 普通图层:检查是否有对象 return layer.fabricObjects && layer.fabricObjects.length > 0; } } /** * 导出图层 -- 下载图层图片 * @param {string} layerId 图层ID,默认使用当前活动图层 */ async exportLayerToImage(layerId = null) { const targetLayerId = layerId || this.activeLayerId.value; if (!targetLayerId) { console.warn(this.t("没有指定要栅格化的图层")); return false; } // 查找目标图层 // const targetLayer = this.getLayerById(targetLayerId); const { layer: targetLayer } = findLayerRecursively( this.layers.value, targetLayerId ); if (!targetLayer) { console.error(this.t("图层不存在", { layerId: targetLayerId })); return false; } // 直接创建和执行导出命令 const command = new ExportLayerToImageCommand({ canvas: this.canvas, layers: this.layers, layerId: targetLayerId, activeLayerId: this.activeLayerId, layerManager: this, }); command.undoable = false; // 导出操作通常不需要撤销 // 执行命令 if (this.commandManager) { const result = await this.commandManager.execute(command); if (result) { console.log(`✅ 成功导出图层: ${targetLayer.name}`); } return result; } else { const result = await command.execute(); if (result) { console.log(`✅ 成功导出图层: ${targetLayer.name}`); } return result; } } /** * 栅格化图层并返回Base64编码 * @param {string} layerId 图层ID,默认使用当前活动图层 * @returns {Promise} Base64编码的图像字符串,失败时返回null */ async getLayerToBase64(layerId = null) { const targetLayerId = layerId || this.activeLayerId.value; if (!targetLayerId) { console.warn(this.t("没有指定要栅格化的图层")); return null; } // 查找目标图层 // const targetLayer = this.getLayerById(targetLayerId); const { layer: targetLayer } = findLayerRecursively( this.layers.value, targetLayerId ); if (!targetLayer) { console.error(this.t("图层不存在", { layerId: targetLayerId })); return null; } // 直接创建和执行导出命令 const command = new ExportLayerToImageCommand({ canvas: this.canvas, layers: this.layers, layerId: targetLayerId, activeLayerId: this.activeLayerId, layerManager: this, }); command.undoable = false; // 导出操作通常不需要撤销 // 执行命令 const result = await command.execute(false); if (result) { console.log(`✅ 成功导出图层: ${targetLayer.name}`); } return result; } /** * 为组图层的活动选择组设置遮罩移动同步(修复版) * @param {fabric.ActiveSelection} activeSelection 活动选择组 * @param {Object} layer 组图层对象 * @private */ _setupGroupMaskMovementSync(activeSelection, layer) { if (!activeSelection || !layer || !layer.clippingMask) { return; } // 记录初始位置 let initialLeft = activeSelection.left; let initialTop = activeSelection.top; // 记录遮罩初始位置 let maskInitialLeft = layer.clippingMask.left || 0; let maskInitialTop = layer.clippingMask.top || 0; // 用于节流和状态管理的变量 let isUpdating = false; let lastUpdateTime = 0; let hasMoved = false; // 追踪是否实际发生了移动 const UPDATE_THRESHOLD = 32; // 约60fps // 移动开始事件处理 const handleMovingStart = (e) => { // 判断活动对象是否只有一个 const isSginleObject = e.target === activeSelection?._objects?.[0]; if (e.target === activeSelection || isSginleObject) { hasMoved = false; // 重置移动状态 console.log("🎯 开始移动组选择对象"); // 记录遮罩初始位置 console.log( "🖼️ 记录遮罩初始位置", `${layer.clippingMask.left || 0}, ${layer.clippingMask.top || 0}` ); // 记录初始位置 initialLeft = isSginleObject ? e.target.left : activeSelection.left; initialTop = isSginleObject ? e.target.top : activeSelection.top; maskInitialLeft = layer.clippingMask.left || 0; maskInitialTop = layer.clippingMask.top || 0; } }; // 移动中事件处理函数(带节流) const handleMoving = (e) => { // 判断活动对象是否只有一个 const isSginleObject = e.target === activeSelection?._objects?.[0]; const target = e.target; if (target === activeSelection || isSginleObject) { hasMoved = true; // 标记发生了移动 const now = Date.now(); // 节流处理,避免过于频繁的更新 if (now - lastUpdateTime < UPDATE_THRESHOLD) { return; } if (isUpdating) { return; } isUpdating = true; lastUpdateTime = now; // 使用 requestAnimationFrame 优化渲染 requestAnimationFrame(() => { try { // 计算移动距离 const deltaX = target.left - initialLeft; const deltaY = target.top - initialTop; // 创建更新遮罩位置的命令 const command = new UpdateGroupMaskPositionCommand({ canvas: this.canvas, layerManager: this, layers: this.layers, layerId: layer.id, deltaX: deltaX, deltaY: deltaY, maskInitialLeft: maskInitialLeft, maskInitialTop: maskInitialTop, isExecuteRealtime: true, isSginleObject, target, }); // 执行实时更新 command.executeRealtime(); } finally { isUpdating = false; } }); } }; // 修改事件处理函数 - 使用 object:modified 替代 object:moved const handleModified = (e) => { const target = e.target; // 判断活动对象是否只有一个 const isSginleObject = e.target === activeSelection?._objects?.[0]; if (isSginleObject) { // 如果是单个对象,不处理 console.log("🚫 单个对象不处理移动完成"); hasMoved = false; // 重置移动状态 return; } if ((target === activeSelection || isSginleObject) && hasMoved) { console.log("✅ 组选择对象移动完成"); // 计算最终移动距离 const deltaX = target.left - initialLeft; const deltaY = target.top - initialTop; // 如果有实际移动,创建可撤销的命令 if (Math.abs(deltaX) > 0.1 || Math.abs(deltaY) > 0.1) { const command = new UpdateGroupMaskPositionCommand({ canvas: this.canvas, layers: this.layers, layerManager: this, layerId: layer.id, deltaX: deltaX, deltaY: deltaY, maskInitialLeft: maskInitialLeft, maskInitialTop: maskInitialTop, activeSelection, isSginleObject, target, }); command.undoable = isSginleObject ? false : true; // 设置为可撤销的命令 // 执行可撤销的命令 if (this.commandManager) { this.commandManager.execute(command); } else { command.execute(); } } hasMoved = false; // 重置移动状态 } }; // 鼠标抬起事件处理 - 备用方案 const handleMouseUp = (e) => { if (hasMoved && this.canvas.getActiveObject() === activeSelection) { console.log("🖱️ 鼠标抬起 - 备用移动完成处理"); handleModified(e); } }; // 清理事件监听器的函数 const cleanup = () => { this.canvas.off("object:moving", handleMoving); this.canvas.off("object:modified", handleModified); this.canvas.off("mouse:down", handleMovingStart); this.canvas.off("mouse:up", handleMouseUp); this.canvas.off("selection:cleared", cleanup); this.canvas.off("selection:updated", cleanup); console.log("🧹 清理组遮罩移动同步事件监听器"); }; // 绑定事件监听器 this.canvas.on("mouse:down", handleMovingStart); this.canvas.on("object:moving", handleMoving); this.canvas.on("object:modified", handleModified); // 使用 modified 替代 moved this.canvas.on("mouse:up", handleMouseUp); // 备用方案 // 当选择被清除或更新时清理事件监听器 this.canvas.on("selection:cleared", cleanup); this.canvas.on("selection:updated", cleanup); console.log("🎨 已设置组遮罩移动同步 - 使用 object:modified 事件"); } }