Files
aida_front/src/component/Canvas/CanvasEditor/managers/LayerManager.js
2025-11-11 17:35:00 +08:00

3380 lines
97 KiB
JavaScript
Raw Blame History

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