From 5cc93aeba4859baf2df0d51d80f4c2be5d5a6e94 Mon Sep 17 00:00:00 2001 From: bighuixiang <472705331@qq.com> Date: Tue, 8 Jul 2025 01:08:45 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E9=80=89=E5=8C=BA?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=96=B0=E5=A2=9E=E7=BB=84=E5=9B=BE?= =?UTF-8?q?=E5=B1=82=E8=87=AA=E7=94=B1=E7=BC=96=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../commands/CrossLevelMoveCommands.js | 767 ++++++++++++++++++ .../commands/RasterizeLayerCommand.js | 319 ++++---- .../components/LayersPanel/LayerItem.vue | 1 + .../components/LayersPanel/LayersList.vue | 253 +++++- .../components/LayersPanel/LayersPanel.vue | 313 +++++++ .../CanvasEditor/managers/LayerManager.js | 28 +- .../CanvasEditor/utils/rasterizedImage.js | 231 +++++- 7 files changed, 1751 insertions(+), 161 deletions(-) create mode 100644 src/component/Canvas/CanvasEditor/commands/CrossLevelMoveCommands.js diff --git a/src/component/Canvas/CanvasEditor/commands/CrossLevelMoveCommands.js b/src/component/Canvas/CanvasEditor/commands/CrossLevelMoveCommands.js new file mode 100644 index 00000000..c9f12343 --- /dev/null +++ b/src/component/Canvas/CanvasEditor/commands/CrossLevelMoveCommands.js @@ -0,0 +1,767 @@ +import { Command } from "./Command"; +import { isGroupLayer } from "../utils/layerHelper"; + +/** + * 跨层级移动命令基类 + */ +export class CrossLevelMoveCommand extends Command { + constructor(options) { + super({ + name: "跨层级移动", + undoable: true, + }); + + this.layers = options.layers; + this.layerManager = options.layerManager; + this.canvas = options.canvas; + + // 移动相关参数 + this.layerId = options.layerId; + this.fromContainerType = options.fromContainerType; // 'root' | 'child' + this.toContainerType = options.toContainerType; // 'root' | 'child' + this.fromParentId = options.fromParentId; + this.toParentId = options.toParentId; + this.newIndex = options.newIndex; + this.oldIndex = options.oldIndex; + + // 保存状态用于撤销 + this.beforeState = null; + this.afterState = null; + } + + /** + * 保存移动前的状态 + */ + saveBeforeState() { + this.beforeState = { + layerStructure: this.cloneLayerStructure(), + fabricObjects: this.saveFabricObjectStates(), + }; + } + + /** + * 保存移动后的状态 + */ + saveAfterState() { + this.afterState = { + layerStructure: this.cloneLayerStructure(), + fabricObjects: this.saveFabricObjectStates(), + }; + } + + /** + * 克隆图层结构 + */ + cloneLayerStructure() { + return JSON.parse( + JSON.stringify( + this.layers.value.map((layer) => ({ + id: layer.id, + name: layer.name, + parentId: layer.parentId, + children: layer.children + ? layer.children.map((child) => ({ + id: child.id, + name: child.name, + parentId: child.parentId, + })) + : [], + isBackground: layer.isBackground, + isFixed: layer.isFixed, + visible: layer.visible, + locked: layer.locked, + })) + ) + ); + } + + /** + * 保存fabric对象状态 + */ + saveFabricObjectStates() { + const states = {}; + + // 只保存fabric对象的容器关系,不保存具体的属性 + this.layers.value.forEach((layer) => { + if (layer.fabricObject) { + states[layer.id] = { + isInCanvas: this.canvas + ? this.canvas.contains(layer.fabricObject) + : false, + parentGroupId: null, + visible: layer.fabricObject.visible, + }; + + // 检查是否在某个组中 + this.layers.value.forEach((otherLayer) => { + if ( + otherLayer.fabricObject && + otherLayer.fabricObject.type === "group" && + otherLayer.fabricObject.contains && + otherLayer.fabricObject.contains(layer.fabricObject) + ) { + states[layer.id].parentGroupId = otherLayer.id; + states[layer.id].isInCanvas = false; + } + }); + } + }); + + return states; + } + + /** + * 查找图层 + */ + findLayer(layerId, containerType, parentId) { + if (containerType === "root") { + return this.layers.value.find((layer) => layer.id === layerId); + } else if (containerType === "child" && parentId) { + const parent = this.layers.value.find((layer) => layer.id === parentId); + return parent?.children?.find((child) => child.id === layerId); + } + return null; + } + + /** + * 验证移动的有效性 + */ + validateMove() { + const layer = this.findLayer( + this.layerId, + this.fromContainerType, + this.fromParentId + ); + + if (!layer) { + throw new Error(`找不到要移动的图层: ${this.layerId}`); + } + + // 检查是否为不可移动的图层 + if (layer.isBackground || layer.isFixed) { + throw new Error("背景层和固定层不能移动"); + } + + // 检查是否试图将图层移动到自己的子层级中(防止循环引用) + if ( + this.toContainerType === "child" && + this.toParentId && + this.isDescendantOf(this.toParentId, this.layerId) + ) { + throw new Error("不能将图层移动到自己的子层级中"); + } + + // 检查目标父图层是否为组 + if (this.toContainerType === "child") { + const targetParent = this.layers.value.find( + (layer) => layer.id === this.toParentId + ); + if (!targetParent || !isGroupLayer(targetParent)) { + throw new Error("目标图层不是组图层"); + } + } + + return layer; + } + + /** + * 检查是否为子层级关系(防止循环引用) + */ + isDescendantOf(ancestorId, layerId) { + const checkChildren = (children) => { + if (!children) return false; + for (const child of children) { + if (child.id === layerId) return true; + if (child.children && checkChildren(child.children)) return true; + } + return false; + }; + + const ancestor = this.layers.value.find((layer) => layer.id === ancestorId); + return ancestor ? checkChildren(ancestor.children) : false; + } + + execute() { + console.log("🎯 执行跨层级移动命令:", { + layerId: this.layerId, + from: this.fromContainerType, + to: this.toContainerType, + fromParentId: this.fromParentId, + toParentId: this.toParentId, + }); + + // 验证移动 + const draggedLayer = this.validateMove(); + + // 保存移动前状态 + this.saveBeforeState(); + + try { + // 根据移动类型执行对应操作 + if ( + this.fromContainerType === "root" && + this.toContainerType === "child" + ) { + this.moveRootToGroup(draggedLayer); + } else if ( + this.fromContainerType === "child" && + this.toContainerType === "root" + ) { + this.moveGroupToRoot(draggedLayer); + } else if ( + this.fromContainerType === "child" && + this.toContainerType === "child" && + this.fromParentId !== this.toParentId + ) { + this.moveGroupToGroup(draggedLayer); + } + + // 保存移动后状态 + this.saveAfterState(); + + // 刷新画布 + if (this.canvas) { + this.canvas.renderAll(); + console.log("🎨 画布已刷新"); + } + + console.log("✅ 跨层级移动命令执行成功"); + return true; + } catch (error) { + console.error("❌ 跨层级移动命令执行失败:", error); + + // 如果执行失败,尝试恢复到之前的状态 + if (this.beforeState) { + try { + this.restoreLayerStructure(this.beforeState.layerStructure); + this.restoreFabricObjectStates(this.beforeState.fabricObjects); + if (this.canvas) { + this.canvas.renderAll(); + } + console.log("🔄 已恢复到移动前状态"); + } catch (restoreError) { + console.error("❌ 恢复状态失败:", restoreError); + } + } + + throw error; + } + } + + undo() { + if (!this.beforeState) { + throw new Error("没有保存的前置状态,无法撤销"); + } + + console.log("🔄 撤销跨层级移动命令"); + + try { + // 恢复图层结构 + this.restoreLayerStructure(this.beforeState.layerStructure); + + // 恢复fabric对象状态 + this.restoreFabricObjectStates(this.beforeState.fabricObjects); + + // 刷新画布 + if (this.canvas) { + // 使用 requestAnimationFrame 确保DOM更新完成后再渲染 + requestAnimationFrame(() => { + this.canvas.renderAll(); + }); + } + + console.log("✅ 跨层级移动命令撤销成功"); + return true; + } catch (error) { + console.error("❌ 跨层级移动命令撤销失败:", error); + + // 尝试基本的恢复操作 + try { + console.log("🔄 尝试基本恢复操作"); + this.basicRestore(); + } catch (restoreError) { + console.error("❌ 基本恢复也失败:", restoreError); + } + + throw error; + } + } + + /** + * 基本恢复操作(当标准撤销失败时使用) + */ + basicRestore() { + // 重新组织所有图层的fabric对象 + this.layers.value.forEach((layer) => { + if (layer.fabricObject) { + try { + // 先从所有容器中移除 + this.removeFromAllContainers(layer.fabricObject); + + // 根据图层的parentId重新放置 + if (layer.parentId) { + const parentLayer = this.layers.value.find( + (l) => l.id === layer.parentId + ); + if ( + parentLayer && + parentLayer.fabricObject && + parentLayer.fabricObject.type === "group" + ) { + this.safeAddToContainer( + layer.fabricObject, + parentLayer.fabricObject, + `父组${layer.parentId}` + ); + } + } else { + this.safeAddToContainer(layer.fabricObject, this.canvas, "画布"); + } + } catch (error) { + console.error(`❌ 基本恢复图层 ${layer.id} 失败:`, error); + } + } + }); + + if (this.canvas) { + this.canvas.renderAll(); + } + } + + /** + * 重做命令 + */ + redo() { + if (!this.afterState) { + throw new Error("没有保存的后置状态,无法重做"); + } + + console.log("🔄 重做跨层级移动命令"); + + try { + // 恢复图层结构 + this.restoreLayerStructure(this.afterState.layerStructure); + + // 恢复fabric对象状态 + this.restoreFabricObjectStates(this.afterState.fabricObjects); + + // 刷新画布 + if (this.canvas) { + // 使用 requestAnimationFrame 确保DOM更新完成后再渲染 + requestAnimationFrame(() => { + this.canvas.renderAll(); + }); + } + + console.log("✅ 跨层级移动命令重做成功"); + return true; + } catch (error) { + console.error("❌ 跨层级移动命令重做失败:", error); + + // 尝试基本的恢复操作 + try { + console.log("🔄 尝试基本恢复操作"); + this.basicRestore(); + } catch (restoreError) { + console.error("❌ 基本恢复也失败:", restoreError); + } + + throw error; + } + } + + /** + * 恢复图层结构 + */ + restoreLayerStructure(layerStructure) { + // 保存原始图层数组的引用 + const originalLayers = [...this.layers.value]; + + // 清空当前图层 + this.layers.value.splice(0); + + // 重建图层结构 + layerStructure.forEach((layerData) => { + // 首先从原始图层数组中查找图层 + let layer = originalLayers.find((l) => l.id === layerData.id); + + // 如果没找到,尝试在嵌套结构中查找 + if (!layer) { + layer = this.findLayerInOriginalStructure(layerData.id, originalLayers); + } + + if (layer) { + // 更新图层属性 + layer.parentId = layerData.parentId; + layer.visible = layerData.visible; + layer.locked = layerData.locked; + + // 重建children数组 + if (layerData.children && layerData.children.length > 0) { + layer.children = layerData.children + .map((childData) => { + // 从原始图层数组中查找子图层 + let childLayer = originalLayers.find( + (l) => l.id === childData.id + ); + if (!childLayer) { + childLayer = this.findLayerInOriginalStructure( + childData.id, + originalLayers + ); + } + if (childLayer) { + // 更新子图层属性 + childLayer.parentId = childData.parentId; + childLayer.visible = childData.visible; + childLayer.locked = childData.locked; + return childLayer; + } + return null; + }) + .filter(Boolean); + } else { + layer.children = []; + } + + this.layers.value.push(layer); + } + }); + } + + /** + * 在原始结构中查找图层 + */ + findLayerInOriginalStructure(layerId, layers = null) { + const layersToSearch = layers || this.layers.value; + + const findInLayers = (layersList) => { + for (const layer of layersList) { + if (layer.id === layerId) return layer; + if (layer.children) { + const found = findInLayers(layer.children); + if (found) return found; + } + } + return null; + }; + + return findInLayers(layersToSearch); + } + + /** + * 恢复fabric对象状态 + */ + restoreFabricObjectStates(fabricStates) { + if (!this.canvas || !fabricStates) return; + + console.log("🔧 开始恢复fabric对象状态"); + + // 重新组织fabric对象的层级关系 + this.layers.value.forEach((layer) => { + if (layer.fabricObject && fabricStates[layer.id]) { + const targetState = fabricStates[layer.id]; + console.log(`🔍 恢复图层 ${layer.id} 的fabric对象状态:`, targetState); + + try { + // 首先从所有容器中移除对象 + this.removeFromAllContainers(layer.fabricObject); + + // 根据目标状态放置对象 + if (targetState.parentGroupId) { + // 应该在特定组中 + const parentLayer = this.layers.value.find( + (l) => l.id === targetState.parentGroupId + ); + if ( + parentLayer && + parentLayer.fabricObject && + parentLayer.fabricObject.type === "group" + ) { + this.safeAddToContainer( + layer.fabricObject, + parentLayer.fabricObject, + `父组${targetState.parentGroupId}` + ); + } + } else if (targetState.isInCanvas) { + // 应该在画布中 + this.safeAddToContainer(layer.fabricObject, this.canvas, "画布"); + } + + // 恢复可见性 + if (layer.fabricObject.visible !== targetState.visible) { + layer.fabricObject.visible = targetState.visible; + } + } catch (error) { + console.error(`❌ 恢复图层 ${layer.id} 的fabric对象时出错:`, error); + } + } + }); + + // 强制重新渲染画布 + if (this.canvas) { + this.canvas.renderAll(); + console.log("✅ 画布已重新渲染"); + } + } + + /** + * 安全地处理fabric对象操作 + */ + safeFabricOperation(operation, operationName = "fabric操作") { + try { + return operation(); + } catch (error) { + console.error(`❌ ${operationName}失败:`, error); + return false; + } + } + + /** + * 安全地添加fabric对象到容器 + */ + safeAddToContainer(fabricObject, container, containerName) { + return this.safeFabricOperation(() => { + if (container && container.add && !container.contains(fabricObject)) { + container.add(fabricObject); + console.log(`✅ 成功将对象添加到${containerName}`); + return true; + } + return false; + }, `添加对象到${containerName}`); + } + + /** + * 安全地从容器移除fabric对象 + */ + safeRemoveFromContainer(fabricObject, container, containerName) { + return this.safeFabricOperation(() => { + if ( + container && + container.remove && + container.contains && + container.contains(fabricObject) + ) { + container.remove(fabricObject); + console.log(`✅ 成功从${containerName}移除对象`); + return true; + } + return false; + }, `从${containerName}移除对象`); + } + + /** + * 从所有容器中移除fabric对象 + */ + removeFromAllContainers(fabricObject) { + // 从画布中移除 + this.safeRemoveFromContainer(fabricObject, this.canvas, "画布"); + + // 从所有组中移除 + this.layers.value.forEach((layer) => { + if (layer.fabricObject && layer.fabricObject.type === "group") { + this.safeRemoveFromContainer( + fabricObject, + layer.fabricObject, + `组${layer.id}` + ); + } + }); + } + + /** + * 从顶级图层移动到组图层内 + */ + moveRootToGroup(draggedLayer) { + console.log("📥 执行顶级图层移动到组内:", { + layerId: draggedLayer.id, + toParentId: this.toParentId, + newIndex: this.newIndex, + }); + + const targetParent = this.layers.value.find( + (layer) => layer.id === this.toParentId + ); + if (!targetParent) { + throw new Error(`找不到目标父图层: ${this.toParentId}`); + } + + // 确保父图层有children数组 + if (!targetParent.children) { + targetParent.children = []; + } + + // 从顶级图层数组中移除 + const rootIndex = this.layers.value.findIndex( + (layer) => layer.id === draggedLayer.id + ); + if (rootIndex !== -1) { + this.layers.value.splice(rootIndex, 1); + console.log(`🗑️ 从顶级图层数组中移除图层 ${draggedLayer.id}`); + } + + // 更新图层关系 + draggedLayer.parentId = this.toParentId; + targetParent.children.splice(this.newIndex, 0, draggedLayer); + console.log( + `📂 将图层 ${draggedLayer.id} 添加到父图层 ${this.toParentId} 的children中` + ); + + // 处理fabric对象的层级关系 + if (draggedLayer.fabricObject && targetParent.fabricObject) { + console.log(`🎨 处理fabric对象层级关系`); + + // 从画布中移除 + this.safeRemoveFromContainer( + draggedLayer.fabricObject, + this.canvas, + "画布" + ); + + // 添加到父组 + this.safeAddToContainer( + draggedLayer.fabricObject, + targetParent.fabricObject, + `父组${this.toParentId}` + ); + } + + console.log("✅ 顶级图层移动到组内完成"); + } + + /** + * 从组图层内移动到顶级图层 + */ + moveGroupToRoot(draggedLayer) { + console.log("📤 执行组内图层移动到顶级:", { + layerId: draggedLayer.id, + fromParentId: this.fromParentId, + newIndex: this.newIndex, + }); + + const sourceParent = this.layers.value.find( + (layer) => layer.id === this.fromParentId + ); + if (!sourceParent || !sourceParent.children) { + throw new Error(`找不到源父图层或其children数组: ${this.fromParentId}`); + } + + // 从源父图层的children中移除 + const childIndex = sourceParent.children.findIndex( + (child) => child.id === draggedLayer.id + ); + if (childIndex !== -1) { + sourceParent.children.splice(childIndex, 1); + console.log( + `🗑️ 从父图层 ${this.fromParentId} 的children中移除图层 ${draggedLayer.id}` + ); + } + + // 处理fabric对象的层级关系 + if (draggedLayer.fabricObject && sourceParent.fabricObject) { + console.log(`🎨 处理fabric对象层级关系`); + + // 从父组中移除 + this.safeRemoveFromContainer( + draggedLayer.fabricObject, + sourceParent.fabricObject, + `父组${this.fromParentId}` + ); + + // 添加到画布 + this.safeAddToContainer(draggedLayer.fabricObject, this.canvas, "画布"); + } + + // 清除图层的parentId + delete draggedLayer.parentId; + console.log(`🔗 清除图层 ${draggedLayer.id} 的parentId`); + + // 计算在顶级图层中的插入位置 + const targetIndex = Math.min(this.newIndex, this.layers.value.length); + this.layers.value.splice(targetIndex, 0, draggedLayer); + console.log( + `📂 将图层 ${draggedLayer.id} 添加到顶级图层数组位置 ${targetIndex}` + ); + + console.log("✅ 组内图层移动到顶级完成"); + } + + /** + * 在不同组之间移动 + */ + moveGroupToGroup(draggedLayer) { + console.log("🔄 执行在不同组间移动:", { + layerId: draggedLayer.id, + fromParentId: this.fromParentId, + toParentId: this.toParentId, + newIndex: this.newIndex, + }); + + const sourceParent = this.layers.value.find( + (layer) => layer.id === this.fromParentId + ); + const targetParent = this.layers.value.find( + (layer) => layer.id === this.toParentId + ); + + if (!sourceParent || !targetParent) { + throw new Error("找不到源父图层或目标父图层"); + } + + // 从源父图层中移除 + const childIndex = sourceParent.children.findIndex( + (child) => child.id === draggedLayer.id + ); + if (childIndex !== -1) { + sourceParent.children.splice(childIndex, 1); + console.log( + `🗑️ 从源父图层 ${this.fromParentId} 中移除图层 ${draggedLayer.id}` + ); + + // 从源父组的fabricObject中移除 + if (draggedLayer.fabricObject && sourceParent.fabricObject) { + this.safeRemoveFromContainer( + draggedLayer.fabricObject, + sourceParent.fabricObject, + `源父组${this.fromParentId}` + ); + } + } + + // 更新图层的parentId + draggedLayer.parentId = this.toParentId; + console.log( + `🔗 更新图层 ${draggedLayer.id} 的parentId为 ${this.toParentId}` + ); + + // 确保目标父图层有children数组 + if (!targetParent.children) { + targetParent.children = []; + } + + // 将图层添加到目标组 + targetParent.children.splice(this.newIndex, 0, draggedLayer); + console.log( + `📂 将图层 ${draggedLayer.id} 添加到目标父图层 ${this.toParentId} 的位置 ${this.newIndex}` + ); + + // 将图层的fabricObject添加到目标父组中 + if (draggedLayer.fabricObject && targetParent.fabricObject) { + this.safeAddToContainer( + draggedLayer.fabricObject, + targetParent.fabricObject, + `目标父组${this.toParentId}` + ); + } + + console.log("✅ 不同组间移动完成"); + } +} + +/** + * 创建跨层级移动命令的工厂函数 + */ +export function createCrossLevelMoveCommand(options) { + return new CrossLevelMoveCommand(options); +} diff --git a/src/component/Canvas/CanvasEditor/commands/RasterizeLayerCommand.js b/src/component/Canvas/CanvasEditor/commands/RasterizeLayerCommand.js index b437e662..655aac41 100644 --- a/src/component/Canvas/CanvasEditor/commands/RasterizeLayerCommand.js +++ b/src/component/Canvas/CanvasEditor/commands/RasterizeLayerCommand.js @@ -46,35 +46,6 @@ export class RasterizeLayerCommand extends Command { this.parentLayer = parent; this.isGroupLayer = this.layer?.children && this.layer.children.length > 0; - // 保存原始状态用于撤销 - this.originalLayers = [...this.layers.value]; - this.originalCanvasObjects = [...this.canvas.getObjects()]; - this.originalObjectStates = new Map(); - - // 栅格化结果 - this.rasterizedImage = null; - this.rasterizedImageId = null; - // 生成新图层ID - this.rasterizedLayerId = generateId("rasterized_layer_"); - this.resterizedId = generateId("rasterized_"); - - this.rasterizedLayer = null; - - // 要栅格化的图层和对象 - this.layersToRasterize = []; - this.objectsToRasterize = []; - } - - async execute() { - // 查找目标图层 - const { layer, parent } = findLayerRecursively( - this.layers.value, - this.layerId - ); - this.layer = layer; - this.parentLayer = parent; - this.isGroupLayer = this.layer?.children && this.layer.children.length > 0; - if (!this.layer) { throw new Error(`图层 ${this.layerId} 不存在`); } @@ -84,24 +55,42 @@ export class RasterizeLayerCommand extends Command { throw new Error("背景图层和固定图层不能栅格化"); } + // 生成新图层ID + this.rasterizedLayerId = generateId("rasterized_layer_"); + this.resterizedId = generateId("rasterized_"); + this.rasterizedLayer = null; + + // 要栅格化的图层和对象 + this.layersToRasterize = []; + this.objectsToRasterize = []; + + // 在构造函数中收集相关对象和保存状态 + this._initializeCommand(); + } + + async execute() { + if (!this.layer) { + throw new Error(`图层 ${this.layerId} 不存在`); + } + + if (this.objectsToRasterize.length === 0) { + throw new Error("图层没有内容可栅格化"); + } + try { - // 收集要栅格化的图层和对象 - this._collectLayersAndObjects(); - - if (this.objectsToRasterize.length === 0) { - throw new Error("图层没有内容可栅格化"); - } - - // 保存原始对象状态 - this._saveOriginalObjectStates(); - this.canvas.discardActiveObject(); this.canvas.renderAll(); + // 检查是否有遮罩对象 + const maskObject = this._getMaskObject(); + // 创建栅格化图像 const rasterizedImage = await createRasterizedImage({ canvas: this.canvas, fabricObjects: this.objectsToRasterize, + maskObject: maskObject, + preserveOriginalQuality: true, // 保持原始质量 + isGroupWithMask: this.isGroupLayer && !!maskObject, // 标记是否为带遮罩的组 }); // 创建新的栅格化图层并替换原图层 @@ -123,30 +112,36 @@ export class RasterizeLayerCommand extends Command { } async undo() { - if (!this.originalLayers || !this.originalCanvasObjects) { + if (!this.originalObjectStates || this.originalObjectStates.size === 0) { throw new Error("没有可恢复的原始数据"); } try { await optimizeCanvasRendering(this.canvas, async () => { - // 清空画布 - this.canvas.discardActiveObject(); - this.canvas.clear(); + // 移除栅格化后的图像对象 + if (this.rasterizedImage) { + removeCanvasObjectByObject(this.canvas, this.rasterizedImage); + } - // 恢复原始对象及其状态 - this.originalCanvasObjects.forEach((obj) => { - // 如果保存了该对象的原始状态,则恢复状态 - if (this.originalObjectStates.has(obj.id)) { - const originalState = this.originalObjectStates.get(obj.id); - obj.set(originalState); + // 恢复原始对象 + this.originalObjectStates.forEach((originalState, objectId) => { + const { object } = findObjectById(this.canvas, objectId); + if (object) { + // 恢复对象状态 + object.set(originalState); + object.setCoords(); + } else { + // 如果对象不在画布上,重新添加 + const originalObject = originalState._fabricObject; + if (originalObject) { + this.canvas.add(originalObject); + originalObject.setCoords(); + } } - - this.canvas.add(obj); - obj.setCoords(); }); // 恢复原始图层结构 - this.layers.value = [...this.originalLayers]; + this.layers.value = [...this.originalLayerStructure]; // 恢复原活动图层 this.activeLayerId.value = this.layerId; @@ -163,6 +158,67 @@ export class RasterizeLayerCommand extends Command { } } + /** + * 初始化命令,收集相关图层和对象,保存原始状态 + * @private + */ + _initializeCommand() { + // 收集要栅格化的图层和对象 + this._collectLayersAndObjects(); + + // 保存原始图层结构(深拷贝相关部分) + this._saveOriginalLayerStructure(); + + // 保存原始对象状态 + this._saveOriginalObjectStates(); + } + + /** + * 保存原始图层结构 + * @private + */ + _saveOriginalLayerStructure() { + // 只保存相关的图层结构,而不是整个图层数组 + this.originalLayerStructure = JSON.parse(JSON.stringify(this.layers.value)); + } + + /** + * 保存原始对象状态 + * @private + */ + _saveOriginalObjectStates() { + this.originalObjectStates = new Map(); + + this.objectsToRasterize.forEach((object) => { + if (object && object.id) { + // 深拷贝对象的重要属性 + const originalState = { + left: object.left, + top: object.top, + scaleX: object.scaleX, + scaleY: object.scaleY, + angle: object.angle, + flipX: object.flipX, + flipY: object.flipY, + opacity: object.opacity, + originX: object.originX, + originY: object.originY, + layerId: object.layerId, + layerName: object.layerName, + width: object.width, + height: object.height, + strokeWidth: object.strokeWidth, + visible: object.visible, + // 保存对象引用以便撤销时重新添加 + _fabricObject: object, + }; + this.originalObjectStates.set(object.id, originalState); + } + }); + + console.log(`💾 保存了 ${this.originalObjectStates.size} 个对象的原始状态`); + } + /** * 收集要栅格化的图层和对象 * @private @@ -239,42 +295,15 @@ export class RasterizeLayerCommand extends Command { return result; } - /** - * 保存原始对象状态 - * @private - */ - _saveOriginalObjectStates() { - this.objectsToRasterize.forEach((object) => { - if (object && object.id) { - const originalState = { - left: object.left, - top: object.top, - scaleX: object.scaleX, - scaleY: object.scaleY, - angle: object.angle, - flipX: object.flipX, - flipY: object.flipY, - opacity: object.opacity, - originX: object.originX, - originY: object.originY, - layerId: object.layerId, - layerName: object.layerName, - width: object.width, - height: object.height, - strokeWidth: object.strokeWidth, - visible: object.visible, - }; - this.originalObjectStates.set(object.id, originalState); - } - }); - } - /** * 创建栅格化图层并替换原图层 * @param {fabric.Image} rasterizedImage 栅格化后的图像 * @private */ async _createRasterizedLayer(rasterizedImage) { + // 保存栅格化图像的引用用于撤销 + this.rasterizedImage = rasterizedImage; + // 从画布中移除原有对象 this.objectsToRasterize.forEach((obj) => { removeCanvasObjectByObject(this.canvas, obj); @@ -304,6 +333,21 @@ export class RasterizeLayerCommand extends Command { }); // 在适当位置添加新的栅格化图层 + this._replaceLayerInStructure(); + + // 设置为活动图层 + this.activeLayerId.value = this.rasterizedLayerId; + + await this.layerManager?.updateLayersObjectsInteractivity(false); + + console.log(`🎨 栅格化图层 ${this.rasterizedLayer.name} 创建完成`); + } + + /** + * 在图层结构中替换原图层 + * @private + */ + _replaceLayerInStructure() { // 1.当前如果是子图层,则插入到子图层的位置 const { layer, parent } = findLayerRecursively( this.layers.value, @@ -334,35 +378,37 @@ export class RasterizeLayerCommand extends Command { 1, this.rasterizedLayer ); - } else this.layers.value.splice(insertIndex, 1, this.rasterizedLayer); + } else { + this.layers.value.splice(insertIndex, 1, this.rasterizedLayer); + } } else { // 2.如果没有找到父图层,则添加到顶层 this.layers.value.unshift(this.rasterizedLayer); } + } - // // 替换图层结构 - // if (this.isGroupLayer) { - // // 组图层:移除所有相关图层 - // const layerIdsToRemove = this.layersToRasterize.map((layer) => layer.id); - // this.layers.value = this.layers.value.filter( - // (layer) => !layerIdsToRemove.includes(layer.id) - // ); - // } else { - // // 普通图层:移除原图层 - // const layerIndex = this.layers.value.findIndex( - // (l) => l.id === this.layerId - // ); - // if (layerIndex !== -1) { - // this.layers.value.splice(layerIndex, 1); - // } - // } + /** + * 获取遮罩对象 + * @returns {Object|null} 遮罩对象或null + * @private + */ + _getMaskObject() { + // 如果图层有clippingMask,获取对应的fabric对象 + if (this.layer?.clippingMask?.id) { + const { object: maskObject } = findObjectById( + this.canvas, + this.layer.clippingMask.id + ); + if (maskObject) { + console.log( + `📎 找到遮罩对象: ${maskObject.id}, 类型: ${maskObject.type}` + ); + return maskObject; + } + } - // 设置为活动图层 - this.activeLayerId.value = this.rasterizedLayerId; - - await this.layerManager?.updateLayersObjectsInteractivity(false); - - console.log(`🎨 栅格化图层 ${this.rasterizedLayer.name} 创建完成`); + console.log("📎 未找到遮罩对象"); + return null; } getInfo() { @@ -387,11 +433,11 @@ export class ExportLayerToImageCommand extends Command { constructor(options) { super({ name: "导出图层", - saveState: true, + saveState: false, // 导出不需要保存状态 }); this.canvas = options.canvas; this.layers = options.layers; - this.layerId = options.layerId; // 指定要栅格化的图层ID + this.layerId = options.layerId; // 指定要导出的图层ID // 是否包含锁定对象 this.hasLocked = options.hasLocked || true; // 是否包含隐藏对象 @@ -409,48 +455,29 @@ export class ExportLayerToImageCommand extends Command { this.parentLayer = parent; this.isGroupLayer = this.layer?.children && this.layer.children.length > 0; - // 保存原始状态用于撤销 - this.originalLayers = [...this.layers.value]; - this.originalCanvasObjects = [...this.canvas.getObjects()]; - this.originalObjectStates = new Map(); - - // 栅格化结果 - this.rasterizedImage = null; - this.rasterizedImageId = null; - // 生成新图层ID - this.rasterizedLayerId = generateId("rasterized_layer_"); - this.resterizedId = generateId("rasterized_"); - - this.rasterizedLayer = null; - - // 要栅格化的图层和对象 - this.layersToRasterize = []; - this.objectsToRasterize = []; - } - - async execute() { - // 查找目标图层 - const { layer, parent } = findLayerRecursively( - this.layers.value, - this.layerId - ); - this.layer = layer; - this.parentLayer = parent; - this.isGroupLayer = this.layer?.children && this.layer.children.length > 0; - if (!this.layer) { throw new Error(`图层 ${this.layerId} 不存在`); } + // 要导出的图层和对象 + this.layersToRasterize = []; + this.objectsToRasterize = []; + + // 收集相关对象 + this._collectLayersAndObjects(); + } + + async execute() { + if (!this.layer) { + throw new Error(`图层 ${this.layerId} 不存在`); + } + + if (this.objectsToRasterize.length === 0) { + message.error("图层没有内容可导出"); + throw new Error("图层没有内容可导出"); + } + try { - // 收集要栅格化的图层和对象 - this._collectLayersAndObjects(); - - if (this.objectsToRasterize.length === 0) { - message.error("图层没有内容可导出"); - throw new Error("图层没有内容可导出"); - } - // 保存原始对象状态 this.canvas.discardActiveObject(); this.canvas.renderAll(); @@ -563,8 +590,6 @@ export class ExportLayerToImageCommand extends Command { name: this.name, originalLayerId: this.layerId, originalLayerName: this.layer?.name, - rasterizedLayerId: this.rasterizedLayerId, - rasterizedLayerName: this.rasterizedLayer?.name, isGroupLayer: this.isGroupLayer, objectCount: this.objectsToRasterize?.length || 0, }; diff --git a/src/component/Canvas/CanvasEditor/components/LayersPanel/LayerItem.vue b/src/component/Canvas/CanvasEditor/components/LayersPanel/LayerItem.vue index ab9df7b7..5654082c 100644 --- a/src/component/Canvas/CanvasEditor/components/LayersPanel/LayerItem.vue +++ b/src/component/Canvas/CanvasEditor/components/LayersPanel/LayerItem.vue @@ -373,6 +373,7 @@ function findParentLayerId() { 'fixed-layer': layer.isBackground || layer.isFixed, }, ]" + :data-layer-id="layer.id" @click="handleClick" @dblclick="handleDoubleClick" @touchstart="handleTouchStart" diff --git a/src/component/Canvas/CanvasEditor/components/LayersPanel/LayersList.vue b/src/component/Canvas/CanvasEditor/components/LayersPanel/LayersList.vue index 253d19a8..bc2daf5c 100644 --- a/src/component/Canvas/CanvasEditor/components/LayersPanel/LayersList.vue +++ b/src/component/Canvas/CanvasEditor/components/LayersPanel/LayersList.vue @@ -39,6 +39,7 @@ const emit = defineEmits([ "update:editing-name", "root-layers-sort", "child-layers-sort", + "cross-level-move", // 新增:跨层级移动事件 "select-child-layer", "start-child-layer-edit", "child-context-menu", @@ -73,6 +74,79 @@ const forwardEvent = (eventName, ...args) => { // 处理根级图层拖拽排序 const handleRootLayersSort = (event) => { + console.log("🔄 拖拽结束事件:", event); + console.log("📍 当前组件信息:", { + isChild: props.isChild, + parentLayerId: props.parentLayerId, + groupName: props.groupName, + }); + + // 清理拖拽样式 + const sortableContainers = document.querySelectorAll(".sortable-layers"); + sortableContainers.forEach((container) => { + container.classList.remove("drag-mode", "drop-target", "drop-invalid"); + }); + + // 检查是否为跨层级拖拽 + const { from, to, oldIndex, newIndex, item } = event; + const isFromDifferentContainer = from !== to; + + if (isFromDifferentContainer) { + console.log("🔀 检测到跨层级拖拽"); + + // 获取被拖拽的图层ID + const draggedLayerId = + item.getAttribute("data-layer-id") || + props.sortableRootLayers[oldIndex]?.id; + + if (!draggedLayerId) { + console.error("❌ 无法获取被拖拽的图层ID"); + return; + } + + // 获取容器类型信息 + const fromContainerType = from.getAttribute("data-container-type"); + const toContainerType = to.getAttribute("data-container-type"); + const fromParentId = from.getAttribute("data-parent-id"); + const toParentId = to.getAttribute("data-parent-id"); + + console.log("📦 拖拽信息:", { + fromContainerType, + toContainerType, + fromParentId, + toParentId, + draggedLayerId, + }); + + // 验证拖拽的有效性 + if ( + !validateCrossLevelMove( + draggedLayerId, + fromContainerType, + toContainerType, + fromParentId, + toParentId + ) + ) { + console.warn("⚠️ 无效的跨层级移动,操作被取消"); + return; + } + + // 发送跨层级移动事件 + emit("cross-level-move", { + layerId: draggedLayerId, + fromContainerType, + toContainerType, + fromParentId, + toParentId, + newIndex, + oldIndex, + }); + + return; + } + + // 普通的同层级拖拽排序 if (props.isChild) { // 子图层事件处理 // 确保排序只影响当前组图层的children,而不是全局layers @@ -87,6 +161,92 @@ const handleRootLayersSort = (event) => { } }; +// 验证跨层级移动的有效性 +const validateCrossLevelMove = ( + layerId, + fromType, + toType, + fromParentId, + toParentId +) => { + // 查找图层 + const layer = findLayerInHierarchy(layerId, fromType, fromParentId); + + if (!layer) { + console.error("找不到要移动的图层:", layerId); + return false; + } + + // 检查是否为不可移动的图层 + if (layer.isBackground || layer.isFixed) { + console.warn("背景层和固定层不能移动"); + return false; + } + + // 检查是否试图将组图层移动到自己的子层级中(防止循环引用) + if (toType === "child" && toParentId && isDescendantOf(toParentId, layerId)) { + console.warn("不能将图层移动到自己的子层级中"); + return false; + } + + return true; +}; + +// 在层级结构中查找图层 +const findLayerInHierarchy = (layerId, containerType, parentId) => { + if (containerType === "root") { + return props.layers.find((layer) => layer.id === layerId); + } else if (containerType === "child" && parentId) { + const parent = props.layers.find((layer) => layer.id === parentId); + return parent?.children?.find((child) => child.id === layerId); + } + return null; +}; + +// 检查是否为子层级关系(防止循环引用) +const isDescendantOf = (ancestorId, layerId) => { + const checkChildren = (children) => { + if (!children) return false; + for (const child of children) { + if (child.id === layerId) return true; + if (child.children && checkChildren(child.children)) return true; + } + return false; + }; + + const ancestor = props.layers.find((layer) => layer.id === ancestorId); + return ancestor ? checkChildren(ancestor.children) : false; +}; + +// 拖拽开始事件 +const handleDragStart = (event) => { + console.log("🎯 开始拖拽:", event); + const { item } = event; + + // 为拖拽元素添加数据属性 + if (item) { + item.setAttribute("data-dragging", "true"); + + // 添加拖拽相关的样式类 + const sortableContainers = document.querySelectorAll(".sortable-layers"); + sortableContainers.forEach((container) => { + container.classList.add("drag-mode"); + }); + } +}; + +// 拖拽添加事件(当元素被拖拽到新容器时) +const handleDragAdd = (event) => { + console.log("➕ 拖拽添加:", event); + // 这个事件在跨容器拖拽时会触发 +}; + +// 拖拽移除事件(当元素被从容器中移除时) +const handleDragRemove = (event) => { + console.log("➖ 拖拽移除:", event); + // 这个事件在跨容器拖拽时会触发 +}; + const canDeleteComputed = computed(() => { // 如果是子图层,检查父图层是否可以删除 if (props.isChild) { @@ -106,18 +266,33 @@ const canDeleteComputed = computed(() => {