Files
aida_front/src/component/Canvas/CanvasEditor/managers/LayerManager.js

3423 lines
98 KiB
JavaScript
Raw Normal View History

import { BackgroundFillManager } from "./BackgroundFillManager";
2025-06-09 10:25:54 +08:00
import {
AddLayerCommand,
PasteLayerCommand,
RemoveLayerCommand,
MoveLayerCommand,
ToggleLayerVisibilityCommand,
RenameLayerCommand,
LayerLockCommand,
SetLayerOpacityCommand,
SetLayerBlendModeCommand,
MergeLayersCommand,
GroupLayersCommand,
UngroupLayersCommand,
MergeLayerObjectsCommand,
LayerObjectsToGroupCommand,
ReorderLayersCommand,
2025-06-18 11:05:23 +08:00
ToggleChildLayerVisibilityCommand,
RenameChildLayerCommand,
RemoveChildLayerCommand,
ChildLayerLockCommand,
2025-06-09 10:25:54 +08:00
} from "../commands/LayerCommands";
import {
SetActiveLayerCommand,
AddObjectToLayerCommand,
RemoveObjectFromLayerCommand,
2025-06-18 11:05:23 +08:00
SelectAllLayersCommand,
ClearSelectionCommand,
MoveLayerToTopCommand,
MoveLayerToBottomCommand,
2025-06-09 10:25:54 +08:00
} from "../commands/ObjectLayerCommands";
import {
LayerType,
BlendMode,
createLayer,
createBackgroundLayer,
createFixedLayer,
OperationType,
OperationTypes,
2025-06-18 11:05:23 +08:00
findLayerRecursively,
2025-06-09 10:25:54 +08:00
} from "../utils/layerHelper";
import {
CreateBackgroundLayerCommand,
UpdateBackgroundCommand,
BackgroundSizeCommand,
BackgroundSizeWithScaleCommand,
} from "../commands/BackgroundCommands";
2025-06-22 13:52:28 +08:00
import { MergeGroupLayerCommand } from "../commands/GroupCommands";
import {
ExportLayerToImageCommand,
RasterizeLayerCommand,
} from "../commands/RasterizeLayerCommand";
2025-06-18 11:05:23 +08:00
// 导入图层排序相关类和混入
import {
LayerSort,
createLayerSort,
LayerSortMixin,
LayerSortUtils,
} from "../utils/LayerSort";
2025-06-09 10:25:54 +08:00
import CanvasConfig from "../config/canvasConfig";
2025-06-18 11:05:23 +08:00
import { isBoolean, template } from "lodash-es";
import {
findObjectById,
generateId,
2025-07-21 01:17:25 +08:00
insertObjectAtZIndex,
optimizeCanvasRendering,
} from "../utils/helper";
2025-06-18 11:05:23 +08:00
import { message } from "ant-design-vue";
2025-06-22 13:52:28 +08:00
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()
2025-06-09 10:25:54 +08:00
/**
* 图层管理器 - 负责管理画布上的所有图层
* 包含图层的创建删除修改排序等操作
* 现在统一使用命令管理器进行状态管理
2025-06-18 11:05:23 +08:00
* 集成LayerSort高级排序功能
2025-06-09 10:25:54 +08:00
*/
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 国际化函数
2025-06-09 10:25:54 +08:00
*/
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
2025-06-09 10:25:54 +08:00
this.backgroundFillManager = new BackgroundFillManager({
canvas: this.canvas,
layers: this.layers,
commandManager: this.commandManager,
canvasManager: this.canvasManager,
2025-11-11 14:28:41 +08:00
layerManager: this,
});
2025-06-09 10:25:54 +08:00
// 编辑器模式draw(绘画)、select(选择)、pan(拖拽)
this.editorMode = options.editorMode || CanvasConfig.defaultTool;
// 画布尺寸
this.canvasWidth = options.canvasWidth || 800;
this.canvasHeight = options.canvasHeight || 600;
// 默认背景颜色
2025-06-29 23:29:47 +08:00
this.backgroundColor = options.backgroundColor || { value: "#ffffff" };
2025-06-09 10:25:54 +08:00
// 复制粘贴相关
this.clipboardData = null;
// 操作状态标志,用于控制状态保存
this.isExecutingCommand = false;
// 是否正在执行操作
this.operationInProgress = false;
// 红绿图模式相关
2025-06-18 11:05:23 +08:00
this.isRedGreenMode = options.isRedGreenMode || false;
2025-06-09 10:25:54 +08:00
this.redGreenModeManager = null;
2025-06-18 11:05:23 +08:00
// 初始化图层排序工具
this.layerSort = null;
this.initLayerSort();
2025-06-09 10:25:54 +08:00
// 初始化相关命令
this.initCommandManager();
}
/**
* 初始化命令管理器
* 现在直接使用命令类不再需要注册命令
*/
initCommandManager() {
// 命令注册逻辑已移除,现在直接使用命令类实例化和执行
console.log("CommandManager 已初始化,使用直接命令调用模式");
}
2025-06-18 11:05:23 +08:00
/**
* 初始化图层排序工具
* 创建LayerSort实例并绑定到LayerManager
*/
initLayerSort() {
if (this.canvas && this.layers) {
this.layerSort = createLayerSort(this.canvas, this.layers, {
commandManager: this.commandManager,
});
console.log("图层排序工具已初始化");
}
}
2025-06-09 10:25:54 +08:00
/**
* 设置编辑器模式
* @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
*/
2025-07-21 01:17:25 +08:00
async updateLayersObjectsInteractivity(
isUseOptimize = true,
{ isMoveing = false } = {}
) {
2025-06-09 10:25:54 +08:00
if (!this.canvas) return;
2025-06-18 11:05:23 +08:00
if (isUseOptimize) {
// 优化渲染 - 统一批处理 支持异步回调
2025-06-22 13:52:28 +08:00
await optimizeCanvasRendering(this.canvas, async () => {
2025-06-18 11:05:23 +08:00
// 应用图层交互规则
2025-07-21 01:17:25 +08:00
await this._applyInteractionRules({ isMoveing });
2025-06-18 11:05:23 +08:00
});
} else {
// 直接应用图层交互规则
2025-07-21 01:17:25 +08:00
await this._applyInteractionRules({ isMoveing });
2025-06-18 11:05:23 +08:00
}
2025-06-09 10:25:54 +08:00
2025-07-21 01:17:25 +08:00
// 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();
2025-06-18 11:05:23 +08:00
// // 性能优化使用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(); // 确保画布重新渲染 - 同步渲染
// });
}
2025-06-22 13:52:28 +08:00
async _setObjectInteractivity(obj, layer, editorMode) {
2025-06-18 11:05:23 +08:00
// 设置可见性
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;
2025-06-18 11:05:23 +08:00
// 图层状态决定交互性
if (layer.isBackground || obj.isBackground || layer.isFixed) {
//|| layer.isFixed
2025-06-18 11:05:23 +08:00
// 背景层永远不可选择和擦除
obj.selectable = false;
obj.evented = false;
obj.erasable = !layer.locked;
2025-06-18 11:05:23 +08:00
} 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;
}
2025-06-09 10:25:54 +08:00
2025-06-18 11:05:23 +08:00
if (this.isRedGreenMode) {
// 红绿图模式下 所有普通图层都可擦除
obj.erasable =
layer.visible &&
!layer.locked &&
!layer.isBackground &&
!layer.isFixed;
2025-06-18 11:05:23 +08:00
}
2025-06-09 10:25:54 +08:00
2025-06-18 11:05:23 +08:00
// 平移模式下,禁用多选和擦除
if (editorMode === OperationType.PAN) {
obj.selectable = false;
obj.evented = false;
obj.erasable = false;
}
}
2025-06-09 10:25:54 +08:00
2025-06-18 11:05:23 +08:00
// 应用图层视觉属性
if (layer.opacity !== undefined) obj.opacity = layer.opacity;
if (layer.blendMode) obj.globalCompositeOperation = layer.blendMode;
2025-06-09 10:25:54 +08:00
}
// 私有方法:应用交互规则
2025-07-21 01:17:25 +08:00
async _applyInteractionRules({ isMoveing }) {
2025-06-09 10:25:54 +08:00
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) => {
2025-06-18 11:05:23 +08:00
layerMap[childLayer.id] = childLayer;
});
2025-06-09 10:25:54 +08:00
});
// 批量更新对象
2025-06-22 13:52:28 +08:00
objects.forEach(async (obj) => {
2025-06-18 11:05:23 +08:00
const layer = layerMap[obj.layerId];
2025-06-09 10:25:54 +08:00
if (!obj.layerId) {
// 没有关联图层的对象使用默认设置
obj.selectable = false;
obj.evented = false;
obj.erasable = false; // 未关联图层的对象不可擦除
return;
}
if (!layer) return;
2025-06-18 11:05:23 +08:00
// 设置一级图层对象的交互性
2025-06-22 13:52:28 +08:00
await this._setObjectInteractivity(obj, layer, editorMode);
2025-06-09 10:25:54 +08:00
2025-06-18 11:05:23 +08:00
// 设置子图层对象的交互性
layer?.children?.forEach(async (childLayer) => {
const childObj = this.canvas
.getObjects()
.find((o) => o.layerId === childLayer.id);
2025-06-18 11:05:23 +08:00
if (childObj) {
2025-06-22 13:52:28 +08:00
await this._setObjectInteractivity(childObj, childLayer, editorMode);
2025-06-09 10:25:54 +08:00
}
2025-06-18 11:05:23 +08:00
});
2025-06-09 10:25:54 +08:00
});
// 设置裁剪对象
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();
// }
// }
}
2025-07-21 01:17:25 +08:00
// 设置画布选区内容
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
);
}
});
2025-06-09 10:25:54 +08:00
}
/**
* 填充图层背景
* @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
);
}
2025-06-09 10:25:54 +08:00
/**
* 创建新图层
* @param {string} name 图层名称
* @param {string} type 图层类型
* @param {Object} options 额外选项
* @returns {string} 新创建的图层ID
*/
2025-06-22 13:52:28 +08:00
async createLayer(name = null, type = LayerType.EMPTY, options = {}) {
2025-06-09 10:25:54 +08:00
// 生成唯一ID
const layerId = options.id || options.layerId || generateId("layer_");
2025-06-09 10:25:54 +08:00
// 计算普通图层数量(非背景、非固定)
const normalLayersCount = this.layers.value.filter(
(layer) => !layer.isBackground && !layer.isFixed
).length;
2025-06-09 10:25:54 +08:00
// 计算插入位置如果没有指定insertIndex则根据当前选中图层决定插入位置
// 添加到图层列表
// let insertIndex = this._getInsertIndexAboveActiveLayer();
// if (options.insertIndex !== undefined) {
// insertIndex = options.insertIndex;
// }
2025-06-09 10:25:54 +08:00
// 创建新图层
const newLayer = createLayer({
id: layerId,
name: name || `图层 ${normalLayersCount + 1}`,
2025-06-09 10:25:54 +08:00
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,
2025-06-09 10:25:54 +08:00
});
2025-06-18 11:05:23 +08:00
// 如果有额外选项设置命令的undoable属性
command.undoable = options.undoable;
2025-06-09 10:25:54 +08:00
// 如果是第一个图层或者普通图层数量小于等于3设置为不可撤销
if (normalLayersCount < 1) {
2025-06-09 10:25:54 +08:00
command.undoable = false;
}
// 执行命令
if (this.commandManager) {
2025-06-22 13:52:28 +08:00
await this.commandManager.execute(command);
2025-06-09 10:25:54 +08:00
} else {
2025-06-22 13:52:28 +08:00
await command.execute();
2025-06-09 10:25:54 +08:00
}
return layerId;
}
/**
* 创建背景图层
* @param {string} name 图层名称
* @returns {string} 创建的背景层ID
*/
createBackgroundLayer(name = "背景") {
// 检查是否已有背景图层
const hasBackgroundLayer = this.layers.value.some(
(layer) => layer.isBackground
);
2025-06-09 10:25:54 +08:00
if (hasBackgroundLayer) {
console.warn("已存在背景层,不再创建新的背景层");
return null;
}
// 创建背景图层
const bgLayer = createBackgroundLayer({
name: name,
canvasWidth: this.canvasWidth,
canvasHeight: this.canvasHeight,
2025-06-29 23:29:47 +08:00
backgroundColor: this.backgroundColor.value,
2025-06-09 10:25:54 +08:00
});
// 直接创建和执行命令
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
)}`;
2025-06-09 10:25:54 +08:00
// 创建固定图层
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; // 如果没有背景图层,则添加到最后
}
/**
* 初始化图层确保有背景层固定图层和一个空白图层
*/
2025-06-22 13:52:28 +08:00
async initializeLayers() {
2025-06-09 10:25:54 +08:00
// 如果没有任何图层,创建背景层、固定图层和一个空白图层
if (this.layers.value.length === 0) {
// 创建背景图层
this.createBackgroundLayer(this.t("Canvas.Background"));
2025-06-09 10:25:54 +08:00
// 创建固定图层,位于背景图层之上
this.createFixedLayer(this.t("Canvas.FixedLayer"));
2025-06-09 10:25:54 +08:00
// 创建一个空白图层(默认位于背景图层和固定图层之上)
await this.createLayer(this.t("Canvas.Layer1"));
2025-06-09 10:25:54 +08:00
} else {
// 检查是否已有背景层
const hasBackgroundLayer = this.layers.value.some(
(layer) => layer.isBackground
);
2025-06-09 10:25:54 +08:00
if (!hasBackgroundLayer) {
this.createBackgroundLayer(this.t("Canvas.Background"));
2025-06-09 10:25:54 +08:00
}
// 检查是否已有固定图层
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"));
2025-06-09 10:25:54 +08:00
}
}
// 排序图层
this.sortLayers();
// 更新对象交互性
this.updateLayersObjectsInteractivity();
}
/**
* 添加对象到图层
* @param {Object} fabricObject fabric对象
* @param {string} layerId 目标图层ID如果不提供则使用当前活动图层
* @returns {Object} 添加的对象
*/
addObjectToLayer(fabricObject, layerId = null, options = {}) {
2025-06-09 10:25:54 +08:00
const targetLayerId = layerId || this.activeLayerId.value;
// 如果没有指定图层ID也没有活动图层则返回错误
if (!targetLayerId) {
console.warn("没有指定目标图层ID且没有活动图层无法添加对象");
return null;
}
// 验证目标图层是否存在
const { layer: targetLayer } = findLayerRecursively(
this.layers.value,
targetLayerId
);
2025-06-09 10:25:54 +08:00
if (!targetLayer) {
console.error(`目标图层 ${targetLayerId} 不存在`);
return null;
}
// 直接创建和执行命令
const command = new AddObjectToLayerCommand({
canvas: this.canvas,
layers: this.layers,
layerId: targetLayerId,
fabricObject: fabricObject,
2025-06-18 11:05:23 +08:00
layerManager: this,
2025-06-09 10:25:54 +08:00
});
// 设置命令的撤销状态
if (isBoolean(options.undoable)) command.undoable = options.undoable; // 是否撤销
2025-06-09 10:25:54 +08:00
// 执行命令
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;
2025-06-09 10:25:54 +08:00
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
2025-06-18 11:05:23 +08:00
if (layerId === this.activeLayerId.value) {
console.warn("当前图层已是活动图层,无需重复设置");
return;
}
2025-06-09 10:25:54 +08:00
// 直接创建和执行命令
const command = new SetActiveLayerCommand({
layers: this.layers,
canvas: this.canvas,
layerManager: this,
activeLayerId: this.activeLayerId,
layerId: layerId,
2025-06-18 11:05:23 +08:00
parentId: options?.parentId,
2025-06-09 10:25:54 +08:00
editorMode: this.editorMode,
...options,
});
2025-06-18 11:05:23 +08:00
// 如果有额外选项设置命令的undoable属性
command.undoable = !!options.undoable;
2025-06-09 10:25:54 +08:00
// 执行命令
if (this.commandManager) {
this.commandManager.execute(command);
} else {
command.execute();
}
}
/**
* 获取当前活动图层
* @return {Object} layer 图层对象
*/
getActiveLayer() {
2025-06-22 13:52:28 +08:00
if (!this.activeLayerId.value) {
console.warn(
"没有活动图层ID无法获取活动图层 ==== 默认设置第一个图层为活动图层"
);
2025-06-22 13:52:28 +08:00
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);
2025-06-09 10:25:54 +08:00
return null;
}
}
// 获取当前活动图层ID
getActiveLayerId() {
return this.activeLayerId.value;
}
/**
* 根据ID获取图层
* @param {string} layerId 图层ID
* @returns {Object|null} 图层对象或null
*/
getLayerById(layerId) {
2025-07-24 21:37:21 +08:00
const { layer } = findLayerRecursively(
this.layers?.value ?? this.layers,
layerId
);
2025-06-18 11:05:23 +08:00
return layer;
2025-06-09 10:25:54 +08:00
}
/**
* 根据name获取图层
* @param {string} layerName 图层名称
* @returns {Object|null} 图层对象或null
*/
getLayerByName(layerName) {
const layer = this.layers.value.find((layer) => layer.name === layerName);
return layer || null;
}
2025-06-09 10:25:54 +08:00
/**
* 获取当前图层对象的列表
* @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) {
// 查找要删除的图层
2025-09-03 22:03:51 +08:00
const { layer, parent } = findLayerRecursively(this.layers.value, layerId);
2025-06-09 10:25:54 +08:00
// 如果是背景层或固定层,不允许删除
if (layer && (layer.isBackground || layer.isFixed)) {
console.warn(layer.isBackground ? "背景层不可删除" : "固定层不可删除");
2025-06-18 11:05:23 +08:00
message.warning(layer.isBackground ? "背景层不可删除" : "固定层不可删除");
2025-06-09 10:25:54 +08:00
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;
}
2025-09-03 22:03:51 +08:00
// // 如果图层有子图层,提示确认
// if (layer && layer.children && layer.children.length > 0) {
// console.warn("该图层包含子图层,删除将同时删除所有子图层");
// message.warning("该图层包含子图层,删除将同时删除所有子图层");
// }
// 删除的是子图层
if (parent && layer) {
this.removeChildLayer(layer.id, parent.id);
return true;
2025-06-09 10:25:54 +08:00
}
// 直接创建和执行命令
const command = new RemoveLayerCommand({
canvas: this.canvas,
layers: this.layers,
layerId: layerId,
activeLayerId: this.activeLayerId,
2025-07-21 01:17:25 +08:00
layerManager: this,
2025-06-09 10:25:54 +08:00
});
// 执行命令
if (this.commandManager) {
this.commandManager.execute(command);
} else {
command.execute();
}
return true;
}
2025-06-18 11:05:23 +08:00
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;
2025-06-18 11:05:23 +08:00
if (layer.isFixed || layer.isBackground) {
layer.fabricObject = tempFabricObject;
} else {
layer.fabricObjects =
layer.fabricObjects.map((item) => {
if (item.id === tempFabricObject.id) {
// 更新对象属性
return tempFabricObject;
}
return item;
}) || [];
}
}
2025-06-09 10:25:54 +08:00
/**
* 移动图层位置
* @param {string} layerId 图层ID
2025-06-18 11:05:23 +08:00
* @param {string} direction 移动方向'up''down''toTop''toBottom'
2025-06-09 10:25:54 +08:00
* @returns {boolean} 是否移动成功
*/
moveLayer(layerId, direction) {
// 查找要移动的图层
2025-06-18 11:05:23 +08:00
const { layer } = findLayerRecursively(this.layers.value, layerId);
2025-06-09 10:25:54 +08:00
// 如果是背景层或固定层,不允许移动
if (layer && (layer.isBackground || layer.isFixed)) {
console.warn(
layer.isBackground ? this.t("背景层不可移动") : this.t("固定层不可移动")
);
2025-06-09 10:25:54 +08:00
return false;
}
2025-06-18 11:05:23 +08:00
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;
}
2025-06-09 10:25:54 +08:00
// 执行命令
if (this.commandManager) {
2025-06-18 11:05:23 +08:00
const result = this.commandManager.execute(command);
if (result) {
console.log(`✅ 图层移动成功: ${direction}`);
}
return result;
2025-06-09 10:25:54 +08:00
} else {
2025-06-18 11:05:23 +08:00
const result = command.execute();
if (result) {
// 更新画布渲染顺序
console.log(`✅ 图层移动成功: ${direction}`);
}
return result;
2025-06-09 10:25:54 +08:00
}
}
// 设置激活当前图层下画布中的所有对象,并变成选择组
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);
2025-11-10 16:59:28 +08:00
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;
}
2025-06-09 10:25:54 +08:00
/**
* 切换图层可见性
* @param {string} layerId 图层ID
* @returns {boolean} 更新后的可见性状态
*/
2025-06-22 13:52:28 +08:00
async toggleLayerVisibility(layerId) {
2025-06-09 10:25:54 +08:00
// 直接创建和执行命令
const command = new ToggleLayerVisibilityCommand({
canvas: this.canvas,
layers: this.layers,
layerId: layerId,
2025-06-22 13:52:28 +08:00
layerManager: this,
2025-06-09 10:25:54 +08:00
});
// 执行命令
if (this.commandManager) {
2025-06-22 13:52:28 +08:00
return await this.commandManager.execute(command);
2025-06-09 10:25:54 +08:00
} else {
2025-06-22 13:52:28 +08:00
return await command.execute();
2025-06-09 10:25:54 +08:00
}
}
/**
* 查询图层可见性
* @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;
}
2025-06-09 10:25:54 +08:00
/**
* 切换图层锁定状态
* @param {string} layerId 图层ID
* @returns {boolean} 更新后的锁定状态
*/
toggleLayerLock(layerId) {
// 直接创建和执行命令
const command = new LayerLockCommand({
canvas: this.canvas,
layers: this.layers,
layerId: layerId,
2025-06-18 11:05:23 +08:00
layerManager: this,
2025-06-09 10:25:54 +08:00
});
// 执行命令
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<string>} 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<string>} layerIds 要组合的图层ID数组
* @param {string} groupName 组名称可选
* @returns {string} 新组图层ID
*/
2025-06-18 11:05:23 +08:00
async groupLayers(layerIds, groupName = null) {
2025-06-09 10:25:54 +08:00
// 检查参数
if (!layerIds || !Array.isArray(layerIds) || layerIds.length < 2) {
console.error("组合图层至少需要两个图层ID");
return null;
}
// 直接创建和执行命令
const command = new GroupLayersCommand({
canvas: this.canvas,
layers: this.layers,
2025-06-18 11:05:23 +08:00
activeLayerId: this.activeLayerId,
2025-06-09 10:25:54 +08:00
layerIds: layerIds,
groupName: groupName,
});
// 执行命令
if (this.commandManager) {
2025-06-18 11:05:23 +08:00
return await this.commandManager.execute(command);
2025-06-09 10:25:54 +08:00
} else {
2025-06-18 11:05:23 +08:00
return await command.execute();
2025-06-09 10:25:54 +08:00
}
}
/**
* 解组一个组图层
* @param {string} groupId 要解组的组图层ID
* @returns {Array<string>} 解组后的图层ID数组
*/
2025-06-18 11:05:23 +08:00
async ungroupLayers(groupId) {
2025-06-09 10:25:54 +08:00
// 查找组图层
const groupLayer = this.layers.value.find((l) => l.id === groupId);
if (
!groupLayer ||
!groupLayer.children ||
groupLayer.children.length === 0
) {
2025-06-09 10:25:54 +08:00
console.error(`${groupId} 不是有效的组图层或不包含子图层`);
return null;
}
// 直接创建和执行命令
const command = new UngroupLayersCommand({
canvas: this.canvas,
layers: this.layers,
groupId: groupId,
2025-06-18 11:05:23 +08:00
activeLayerId: this.activeLayerId,
2025-06-09 10:25:54 +08:00
});
// 执行命令
if (this.commandManager) {
2025-06-18 11:05:23 +08:00
return await this.commandManager.execute(command);
2025-06-09 10:25:54 +08:00
} else {
2025-06-18 11:05:23 +08:00
return await command.execute();
2025-06-09 10:25:54 +08:00
}
}
/**
* 调整画布和背景图层尺寸
* @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(), // 传递红绿图模式状态
});
2025-06-18 11:05:23 +08:00
command.undoable = options.undoable; // 设置为可撤销
2025-06-09 10:25:54 +08:00
// 执行命令
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);
2025-06-09 10:25:54 +08:00
if (hasOtherElements) {
// 有其他元素时使用带缩放的命令
const command = new BackgroundSizeWithScaleCommand({
canvas: this.canvas,
layers: this.layers,
canvasManager: this.canvasManager,
newWidth: width,
newHeight: height,
isRedGreenMode: this.isInRedGreenMode(), // 传递红绿图模式状态
});
2025-06-18 11:05:23 +08:00
command.undoable = false; // 设置为可撤销
2025-06-09 10:25:54 +08:00
// 执行命令
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() {
2025-06-18 11:05:23 +08:00
if (this.layerSort) {
// 使用LayerSort的高级排序
this.layerSort.rearrangeObjects();
return;
}
// 传统排序逻辑(保持原有逻辑作为备用)
2025-06-09 10:25:54 +08:00
if (!this.canvas) return;
// 获取画布上的所有对象
const canvasObjects = [
...this.canvas.getObjects(["id", "layerId", "layerName"]),
];
2025-06-09 10:25:54 +08:00
// 清空画布
this.canvas.clear();
// 按图层顺序(从底到顶)重新添加对象
// 注意:图层数组是从顶到底的顺序,需要反向遍历
for (let i = this.layers.value.length - 1; i >= 0; i--) {
const layer = this.layers.value[i];
// 跳过不可见图层
if (!layer.visible) continue;
2025-06-18 11:05:23 +08:00
if (layer.isBackground && layer.fabricObject) {
// 背景图层
const originalObj = canvasObjects.find(
(o) => o.id === layer.fabricObject.id
);
2025-06-18 11:05:23 +08:00
if (originalObj) {
this.canvas.add(originalObj);
} else {
this.canvas.add(layer.fabricObject);
2025-06-09 10:25:54 +08:00
}
} else if (
layer.isFixed &&
layer.fabricObjects &&
layer.fabricObjects.length > 0
) {
2025-06-18 11:05:23 +08:00
// 固定图层
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
) {
2025-06-09 10:25:54 +08:00
// 普通图层添加所有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
);
2025-06-09 10:25:54 +08:00
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,
2025-06-29 23:29:47 +08:00
backgroundColor: this.backgroundColor.value,
2025-06-09 10:25:54 +08:00
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);
}
2025-06-09 10:25:54 +08:00
/**
* 复制图层数据到剪贴板
* @param {string} layerId 要复制的图层ID
* @returns {Object} 复制的图层数据
*/
copyLayer(layerId) {
const { layer } = findLayerRecursively(this.layers.value, layerId);
2025-06-09 10:25:54 +08:00
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;
});
2025-06-09 10:25:54 +08:00
}
// 存储到剪贴板
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);
2025-06-09 10:25:54 +08:00
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;
}
// 检查是否是唯一的普通图层
2025-10-17 17:27:59 +08:00
const normalLayers = this.layers.value.filter((l) => !l.isBackground && !l.isFixed);
console.log("普通图层:", normalLayers)
2025-06-09 10:25:54 +08:00
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
2025-06-09 10:25:54 +08:00
)
.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);
2025-06-09 10:25:54 +08:00
if (!this.clipboardData) {
console.error("剪贴板中没有图层数据");
return null;
}
2025-06-18 11:05:23 +08:00
// 创建粘贴图层命令
const command = new PasteLayerCommand({
canvas: this.canvas,
layers: this.layers,
activeLayerId: this.activeLayerId,
clipboardData: this.clipboardData,
layerManager: this, // 传递LayerManager实例
});
2025-06-09 10:25:54 +08:00
// 设置命令为可撤销
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; // 清空剪贴板数据 复制一次就清空,避免重复粘贴 出现 错误后也清空 总之就是清空 不用给自己找麻烦
2025-06-18 11:05:23 +08:00
// 执行命令
return result;
2025-06-09 10:25:54 +08:00
}
/**
2025-06-18 11:05:23 +08:00
* 选择所有图层
2025-06-09 10:25:54 +08:00
*/
2025-06-18 11:05:23 +08:00
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
2025-06-18 11:05:23 +08:00
) || null
);
}
2025-06-09 10:25:54 +08:00
/**
* 获取活动图层上方的插入索引
* @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<string>} 合并后的图像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,
2025-06-18 11:05:23 +08:00
layerManager: this,
2025-06-09 10:25:54 +08:00
fabricImage,
activeLayer,
});
// 执行命令
return command;
}
/**
* 更新背景图层颜色
* @param {string} backgroundColor 背景颜色
*/
updateBackgroundColor(backgroundColor, options = {}) {
const backgroundLayer = this.layers.value.find(
(layer) => layer.isBackground
);
2025-06-09 10:25:54 +08:00
if (!backgroundLayer) {
console.warn("没有找到背景图层");
return;
}
// 直接创建和执行命令
const command = new UpdateBackgroundCommand({
canvas: this.canvas,
layers: this.layers,
canvasManager: this.canvasManager,
layerManger: this,
2025-06-29 23:29:47 +08:00
backgroundColor,
backgroundColorValue: this.backgroundColor,
oldColor: options.oldColor,
2025-06-09 10:25:54 +08:00
});
command.undoable = isBoolean(options.undoable) ? options.undoable : true; // 设置为可撤销
2025-06-09 10:25:54 +08:00
// 执行命令
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<string>} 被清理的图层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() {
// 清空画布
2025-11-13 14:54:50 +08:00
// if (this.canvas) {
// this.canvas.clear();
// }
2025-06-09 10:25:54 +08:00
// 清空图层数据
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} 创建的文本对象
*/
2025-06-22 13:52:28 +08:00
async createTextLayerWithObject(textObject, options = {}) {
2025-06-09 10:25:54 +08:00
if (!this.canvas || !textObject) return null;
// 确保对象有ID
textObject.id =
textObject.id || `text_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
2025-06-09 10:25:54 +08:00
// 创建文本图层
const layerName = options.name || "文本图层";
2025-06-22 13:52:28 +08:00
const layerId = await this.createLayer(layerName, LayerType.TEXT, {
2025-06-09 10:25:54 +08:00
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",
2025-06-09 10:25:54 +08:00
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);
2025-06-09 10:25:54 +08:00
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"])
);
2025-06-09 10:25:54 +08:00
}
// 设置此图层为活动图层
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
);
2025-06-09 10:25:54 +08:00
if (foundObject) {
return layer;
}
}
}
return null;
}
/**
* 拖拽排序图层
* @param {number} oldIndex 原索引
* @param {number} newIndex 新索引
* @param {string} layerId 图层ID
* @returns {boolean} 是否排序成功
*/
2025-06-18 11:05:23 +08:00
async reorderLayers(oldIndex, newIndex, layerId) {
2025-06-09 10:25:54 +08:00
// 检查索引有效性
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,
2025-06-18 11:05:23 +08:00
layerSort: this.layerSort,
2025-06-09 10:25:54 +08:00
});
// 执行命令
if (this.commandManager) {
2025-06-18 11:05:23 +08:00
return await this.commandManager.execute(command);
2025-06-09 10:25:54 +08:00
} else {
2025-06-18 11:05:23 +08:00
return await command.execute();
2025-06-09 10:25:54 +08:00
}
}
/**
* 拖拽排序子图层
* @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
);
2025-06-18 11:05:23 +08:00
}
/**
* 根据图层ID和父图层ID查找子图层
* @param {string} layerId 子图层ID
* @param {string} parentId 父图层ID
* @returns {Object|null} 子图层对象或null
*/
findChildLayer(layerId, parentId) {
2025-06-09 10:25:54 +08:00
const parentLayer = this.getLayerById(parentId);
2025-06-18 11:05:23 +08:00
if (!parentLayer || !parentLayer.children) return null;
2025-06-09 10:25:54 +08:00
2025-06-18 11:05:23 +08:00
return parentLayer.children.find((child) => child.id === layerId) || null;
}
2025-06-09 10:25:54 +08:00
2025-06-18 11:05:23 +08:00
/**
* 删除子图层
* @param {string} layerId 子图层ID
* @param {string} parentId 父图层ID
* @returns {boolean} 是否删除成功
*/
2025-06-22 13:52:28 +08:00
async removeChildLayer(layerId, parentId) {
2025-06-18 11:05:23 +08:00
// 直接创建和执行命令
const command = new RemoveChildLayerCommand({
canvas: this.canvas,
layers: this.layers,
layerId: layerId,
parentId: parentId,
activeLayerId: this.activeLayerId,
layerManager: this,
});
2025-06-09 10:25:54 +08:00
2025-06-18 11:05:23 +08:00
// 执行命令
if (this.commandManager) {
2025-06-22 13:52:28 +08:00
return await this.commandManager.execute(command);
2025-06-18 11:05:23 +08:00
} else {
2025-06-22 13:52:28 +08:00
return await command.execute();
2025-06-09 10:25:54 +08:00
}
2025-06-18 11:05:23 +08:00
}
2025-06-09 10:25:54 +08:00
2025-06-18 11:05:23 +08:00
/**
* 重命名子图层
* @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();
2025-06-09 10:25:54 +08:00
}
2025-06-18 11:05:23 +08:00
}
2025-06-09 10:25:54 +08:00
2025-06-18 11:05:23 +08:00
/**
* 切换子图层锁定状态
* @param {string} layerId 子图层ID
* @param {string} parentId 父图层ID
* @returns {boolean} 更新后的锁定状态
*/
toggleChildLayerLock(layerId, parentId) {
// 直接创建和执行命令
const command = new ChildLayerLockCommand({
canvas: this.canvas,
2025-06-09 10:25:54 +08:00
layers: this.layers,
layerId: layerId,
2025-06-18 11:05:23 +08:00
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} 更新后的可见性状态
*/
2025-06-22 13:52:28 +08:00
async toggleChildLayerVisibility(layerId, parentId) {
2025-06-18 11:05:23 +08:00
// 直接创建和执行命令
const command = new ToggleChildLayerVisibilityCommand({
2025-06-09 10:25:54 +08:00
canvas: this.canvas,
2025-06-18 11:05:23 +08:00
layers: this.layers,
layerId: layerId,
parentId: parentId,
2025-06-22 13:52:28 +08:00
layerManager: this,
2025-06-09 10:25:54 +08:00
});
// 执行命令
if (this.commandManager) {
2025-06-22 13:52:28 +08:00
return await this.commandManager.execute(command);
2025-06-09 10:25:54 +08:00
} else {
2025-06-22 13:52:28 +08:00
return await command.execute();
2025-06-09 10:25:54 +08:00
}
}
2025-06-18 11:05:23 +08:00
// ==================== 红绿图模式相关操作 ====================
2025-06-09 10:25:54 +08:00
// 设置红绿图模式管理器
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;
}
2025-06-18 11:05:23 +08:00
// ==================== 高级图层排序功能 ====================
/**
* 使用LayerSort工具重新排列画布对象
* 这是页面操作排序图层后重建画布顺序的核心方法
* @param {boolean} async 是否使用异步处理默认根据对象数量自动决定
* @returns {Promise<void>|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);
2025-06-18 11:05:23 +08:00
if (shouldUseAsync) {
console.log(
`使用异步排序处理 ${objectsCount} 个对象, ${layersCount} 个图层`
);
2025-06-18 11:05:23 +08:00
return this.layerSort.rearrangeObjectsAsync();
} else {
console.log(
`使用同步排序处理 ${objectsCount} 个对象, ${layersCount} 个图层`
);
2025-06-18 11:05:23 +08:00
this.layerSort.rearrangeObjects();
}
}
/**
* 智能排序图层
* 根据对象类型和位置自动调整图层顺序
* @param {Array<string>} 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"
);
2025-06-18 11:05:23 +08:00
}
const result = this.layerSort.moveLayerToIndex({
parentId,
oldIndex,
newIndex,
layerId,
});
if (result) {
console.log(
`图层 ${layerId} - oldIndex: ${oldIndex} 已移动到位置 ${newIndex}`
);
2025-06-18 11:05:23 +08:00
// 更新对象交互性
// 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
);
2025-06-18 11:05:23 +08:00
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}`
);
2025-06-18 11:05:23 +08:00
// 更新对象交互性
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
);
2025-06-18 11:05:23 +08:00
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<string>} 所有子图层成组的图层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("找不到有效的组图层或组图层为空"));
2025-06-18 11:05:23 +08:00
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<boolean>} 是否栅格化成功
*/
async rasterizeLayer(layerId = null) {
const targetLayerId = layerId || this.activeLayerId.value;
if (!targetLayerId) {
console.warn(this.t("没有指定要栅格化的图层"));
2025-06-18 11:05:23 +08:00
return false;
}
// 查找目标图层
2025-06-22 13:52:28 +08:00
// const targetLayer = this.getLayerById(targetLayerId);
const { layer: targetLayer } = findLayerRecursively(
this.layers.value,
targetLayerId
);
2025-06-22 13:52:28 +08:00
2025-06-18 11:05:23 +08:00
if (!targetLayer) {
console.error(this.t("图层不存在", { layerId: targetLayerId }));
2025-06-18 11:05:23 +08:00
return false;
}
// 不允许栅格化背景图层和固定图层
if (targetLayer.isBackground || targetLayer.isFixed) {
console.warn(this.t("背景图层和固定图层不能栅格化"));
2025-06-18 11:05:23 +08:00
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;
2025-06-18 11:05:23 +08:00
if (!layer) return false;
// 不允许栅格化背景图层和固定图层
if (layer.isBackground || layer.isFixed) {
return false;
}
// 检查图层是否有内容可以栅格化
if (layer.type === "group" || layer.children.length > 0) {
2025-06-18 11:05:23 +08:00
// 组图层:检查是否有子图层且子图层有内容
return layer.children.some(
(child) => child.fabricObjects && child.fabricObjects.length > 0
);
2025-06-18 11:05:23 +08:00
} 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;
}
}
2025-06-18 11:05:23 +08:00
/**
2025-11-12 16:12:02 +08:00
* 栅格化图层并返回Base64编码
* @param {string} layerId 图层ID默认使用当前活动图层
* @returns {Promise<string|null>} 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 组图层对象
2025-06-18 11:05:23 +08:00
* @private
*/
_setupGroupMaskMovementSync(activeSelection, layer) {
if (!activeSelection || !layer || !layer.clippingMask) {
return;
2025-06-18 11:05:23 +08:00
}
// 记录初始位置
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 事件");
2025-06-18 11:05:23 +08:00
}
2025-06-09 10:25:54 +08:00
}