2199 lines
59 KiB
JavaScript
2199 lines
59 KiB
JavaScript
|
|
import {
|
|||
|
|
AddLayerCommand,
|
|||
|
|
PasteLayerCommand,
|
|||
|
|
RemoveLayerCommand,
|
|||
|
|
MoveLayerCommand,
|
|||
|
|
ToggleLayerVisibilityCommand,
|
|||
|
|
RenameLayerCommand,
|
|||
|
|
LayerLockCommand,
|
|||
|
|
SetLayerOpacityCommand,
|
|||
|
|
SetLayerBlendModeCommand,
|
|||
|
|
MergeLayersCommand,
|
|||
|
|
GroupLayersCommand,
|
|||
|
|
UngroupLayersCommand,
|
|||
|
|
MergeLayerObjectsCommand,
|
|||
|
|
LayerObjectsToGroupCommand,
|
|||
|
|
ReorderLayersCommand,
|
|||
|
|
ReorderChildLayersCommand,
|
|||
|
|
} from "../commands/LayerCommands";
|
|||
|
|
import {
|
|||
|
|
SetActiveLayerCommand,
|
|||
|
|
AddObjectToLayerCommand,
|
|||
|
|
RemoveObjectFromLayerCommand,
|
|||
|
|
} from "../commands/ObjectLayerCommands";
|
|||
|
|
import {
|
|||
|
|
LayerType,
|
|||
|
|
BlendMode,
|
|||
|
|
createLayer,
|
|||
|
|
createBackgroundLayer,
|
|||
|
|
createFixedLayer,
|
|||
|
|
OperationType,
|
|||
|
|
OperationTypes,
|
|||
|
|
} from "../utils/layerHelper";
|
|||
|
|
import {
|
|||
|
|
CreateBackgroundLayerCommand,
|
|||
|
|
UpdateBackgroundCommand,
|
|||
|
|
BackgroundSizeCommand,
|
|||
|
|
BackgroundSizeWithScaleCommand,
|
|||
|
|
} from "../commands/BackgroundCommands";
|
|||
|
|
|
|||
|
|
import CanvasConfig from "../config/canvasConfig";
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 图层管理器 - 负责管理画布上的所有图层
|
|||
|
|
* 包含图层的创建、删除、修改、排序等操作
|
|||
|
|
* 现在统一使用命令管理器进行状态管理
|
|||
|
|
*/
|
|||
|
|
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 背景颜色
|
|||
|
|
*/
|
|||
|
|
constructor(options) {
|
|||
|
|
this.canvas = options.canvas;
|
|||
|
|
this.layers = options.layers;
|
|||
|
|
this.activeLayerId = options.activeLayerId;
|
|||
|
|
this.commandManager = options.commandManager;
|
|||
|
|
this.canvasManager = options.canvasManager || null;
|
|||
|
|
|
|||
|
|
// 编辑器模式:draw(绘画)、select(选择)、pan(拖拽)
|
|||
|
|
this.editorMode = options.editorMode || CanvasConfig.defaultTool;
|
|||
|
|
|
|||
|
|
// 画布尺寸
|
|||
|
|
this.canvasWidth = options.canvasWidth || 800;
|
|||
|
|
this.canvasHeight = options.canvasHeight || 600;
|
|||
|
|
|
|||
|
|
// 默认背景颜色
|
|||
|
|
this.backgroundColor = options.backgroundColor || "#ffffff";
|
|||
|
|
|
|||
|
|
// 复制粘贴相关
|
|||
|
|
this.clipboardData = null;
|
|||
|
|
|
|||
|
|
// 操作状态标志,用于控制状态保存
|
|||
|
|
this.isExecutingCommand = false;
|
|||
|
|
// 是否正在执行操作
|
|||
|
|
this.operationInProgress = false;
|
|||
|
|
|
|||
|
|
// 红绿图模式相关
|
|||
|
|
this.isRedGreenMode = false;
|
|||
|
|
this.redGreenModeManager = null;
|
|||
|
|
|
|||
|
|
// 初始化相关命令
|
|||
|
|
this.initCommandManager();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 初始化命令管理器
|
|||
|
|
* 现在直接使用命令类,不再需要注册命令
|
|||
|
|
*/
|
|||
|
|
initCommandManager() {
|
|||
|
|
// 命令注册逻辑已移除,现在直接使用命令类实例化和执行
|
|||
|
|
console.log("CommandManager 已初始化,使用直接命令调用模式");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 设置编辑器模式
|
|||
|
|
* @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
|
|||
|
|
*/
|
|||
|
|
updateLayersObjectsInteractivity() {
|
|||
|
|
if (!this.canvas) return;
|
|||
|
|
|
|||
|
|
// 性能优化:使用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(); // 确保画布重新渲染 - 同步渲染
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 私有方法:应用交互规则
|
|||
|
|
_applyInteractionRules() {
|
|||
|
|
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;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 获取当前活动图层ID
|
|||
|
|
const currentActiveLayerId = this.activeLayerId?.value;
|
|||
|
|
|
|||
|
|
// 批量更新对象
|
|||
|
|
objects.forEach((obj) => {
|
|||
|
|
if (!obj.layerId) {
|
|||
|
|
// 没有关联图层的对象使用默认设置
|
|||
|
|
obj.selectable = false;
|
|||
|
|
obj.evented = false;
|
|||
|
|
obj.erasable = false; // 未关联图层的对象不可擦除
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const layer = layerMap[obj.layerId];
|
|||
|
|
if (!layer) return;
|
|||
|
|
|
|||
|
|
// 设置可见性
|
|||
|
|
obj.visible = layer.visible;
|
|||
|
|
|
|||
|
|
// 判断对象是否在当前活动图层上
|
|||
|
|
const isInActiveLayer = obj.layerId === currentActiveLayerId;
|
|||
|
|
|
|||
|
|
// 基于 fabric-with-erasing 库的 erasable 属性设置擦除权限
|
|||
|
|
// 只有活动图层、可见、非锁定、非背景、非固定图层的对象才可擦除
|
|||
|
|
obj.erasable =
|
|||
|
|
isInActiveLayer &&
|
|||
|
|
layer.visible &&
|
|||
|
|
!layer.locked &&
|
|||
|
|
!layer.isBackground &&
|
|||
|
|
!layer.isFixed;
|
|||
|
|
|
|||
|
|
// 图层状态决定交互性
|
|||
|
|
if (layer.isBackground || obj.isBackground || layer.isFixed) {
|
|||
|
|
// 背景层永远不可选择和擦除
|
|||
|
|
obj.selectable = false;
|
|||
|
|
obj.evented = false;
|
|||
|
|
obj.erasable = false;
|
|||
|
|
} 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 (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;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* 创建新图层
|
|||
|
|
* @param {string} name 图层名称
|
|||
|
|
* @param {string} type 图层类型
|
|||
|
|
* @param {Object} options 额外选项
|
|||
|
|
* @returns {string} 新创建的图层ID
|
|||
|
|
*/
|
|||
|
|
createLayer(name = null, type = LayerType.EMPTY, options = {}) {
|
|||
|
|
// 生成唯一ID
|
|||
|
|
const layerId = `layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
|
|||
|
|
const layerIndex = this.layers.value.length;
|
|||
|
|
|
|||
|
|
// 计算插入位置,如果没有指定insertIndex,则根据当前选中图层决定插入位置
|
|||
|
|
// 添加到图层列表
|
|||
|
|
let insertIndex = this._getInsertIndexAboveActiveLayer();
|
|||
|
|
if (options.insertIndex !== undefined) {
|
|||
|
|
insertIndex = options.insertIndex;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建新图层
|
|||
|
|
const newLayer = createLayer({
|
|||
|
|
id: layerId,
|
|||
|
|
name: name || `图层 ${layerIndex + 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,
|
|||
|
|
insertIndex: insertIndex,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 计算普通图层数量(非背景、非固定)
|
|||
|
|
const normalLayersCount = this.layers.value.filter(
|
|||
|
|
(layer) => !layer.isBackground && !layer.isFixed
|
|||
|
|
).length;
|
|||
|
|
|
|||
|
|
// 如果是第一个图层,或者普通图层数量小于等于3,设置为不可撤销
|
|||
|
|
if (this.layers.value.length === 1 || normalLayersCount <= 3) {
|
|||
|
|
command.undoable = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 执行命令
|
|||
|
|
if (this.commandManager) {
|
|||
|
|
this.commandManager.execute(command);
|
|||
|
|
} else {
|
|||
|
|
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,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 直接创建和执行命令
|
|||
|
|
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; // 如果没有背景图层,则添加到最后
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 创建普通图层
|
|||
|
|
* @param {String} name 图层名称 (默认: null,将生成自动名称)
|
|||
|
|
* @param {String} type 图层类型 (默认: LayerType.EMPTY)
|
|||
|
|
* @param {Object} options 附加选项
|
|||
|
|
* @returns {String} 新创建的图层ID
|
|||
|
|
*/
|
|||
|
|
createLayer(name = null, type = LayerType.EMPTY, options = {}) {
|
|||
|
|
// 生成唯一ID
|
|||
|
|
const layerId = `layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
|
|||
|
|
const layerIndex = this.layers.value.length;
|
|||
|
|
|
|||
|
|
// 计算插入位置,如果没有指定insertIndex,则根据当前选中图层决定插入位置
|
|||
|
|
// 添加到图层列表
|
|||
|
|
let insertIndex = this._getInsertIndexAboveActiveLayer();
|
|||
|
|
if (options.insertIndex !== undefined) {
|
|||
|
|
insertIndex = options.insertIndex;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建新图层
|
|||
|
|
const newLayer = createLayer({
|
|||
|
|
id: layerId,
|
|||
|
|
name: name || `图层 ${layerIndex + 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,
|
|||
|
|
insertIndex: insertIndex,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 如果是第一个图层,设置为不可撤销
|
|||
|
|
if (this.layers.value.length === 1) {
|
|||
|
|
command.undoable = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 执行命令
|
|||
|
|
if (this.commandManager) {
|
|||
|
|
this.commandManager.execute(command);
|
|||
|
|
} else {
|
|||
|
|
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,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 直接创建和执行命令
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 初始化图层,确保有背景层、固定图层和一个空白图层
|
|||
|
|
*/
|
|||
|
|
initializeLayers() {
|
|||
|
|
// 如果没有任何图层,创建背景层、固定图层和一个空白图层
|
|||
|
|
if (this.layers.value.length === 0) {
|
|||
|
|
// 创建背景图层
|
|||
|
|
this.createBackgroundLayer();
|
|||
|
|
|
|||
|
|
// 创建固定图层,位于背景图层之上
|
|||
|
|
this.createFixedLayer();
|
|||
|
|
|
|||
|
|
// 创建一个空白图层(默认位于背景图层和固定图层之上)
|
|||
|
|
this.createLayer("图层 1");
|
|||
|
|
} else {
|
|||
|
|
// 检查是否已有背景层
|
|||
|
|
const hasBackgroundLayer = this.layers.value.some(
|
|||
|
|
(layer) => layer.isBackground
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
if (!hasBackgroundLayer) {
|
|||
|
|
this.createBackgroundLayer();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查是否已有固定图层
|
|||
|
|
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) {
|
|||
|
|
this.createLayer("图层 1");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 排序图层
|
|||
|
|
this.sortLayers();
|
|||
|
|
|
|||
|
|
// 更新对象交互性
|
|||
|
|
this.updateLayersObjectsInteractivity();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 添加对象到图层
|
|||
|
|
* @param {Object} fabricObject fabric对象
|
|||
|
|
* @param {string} layerId 目标图层ID,如果不提供则使用当前活动图层
|
|||
|
|
* @returns {Object} 添加的对象
|
|||
|
|
*/
|
|||
|
|
addObjectToLayer(fabricObject, layerId = null) {
|
|||
|
|
const targetLayerId = layerId || this.activeLayerId.value;
|
|||
|
|
|
|||
|
|
// 如果没有指定图层ID,也没有活动图层,则返回错误
|
|||
|
|
if (!targetLayerId) {
|
|||
|
|
console.warn("没有指定目标图层ID且没有活动图层,无法添加对象");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证目标图层是否存在
|
|||
|
|
const targetLayer = this.getLayerById(targetLayerId);
|
|||
|
|
if (!targetLayer) {
|
|||
|
|
console.error(`目标图层 ${targetLayerId} 不存在`);
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 直接创建和执行命令
|
|||
|
|
const command = new AddObjectToLayerCommand({
|
|||
|
|
canvas: this.canvas,
|
|||
|
|
layers: this.layers,
|
|||
|
|
layerId: targetLayerId,
|
|||
|
|
fabricObject: fabricObject,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 执行命令
|
|||
|
|
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 = {}) {
|
|||
|
|
// 直接创建和执行命令
|
|||
|
|
const command = new SetActiveLayerCommand({
|
|||
|
|
layers: this.layers,
|
|||
|
|
canvas: this.canvas,
|
|||
|
|
layerManager: this,
|
|||
|
|
activeLayerId: this.activeLayerId,
|
|||
|
|
layerId: layerId,
|
|||
|
|
editorMode: this.editorMode,
|
|||
|
|
...options,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 执行命令
|
|||
|
|
if (this.commandManager) {
|
|||
|
|
this.commandManager.execute(command);
|
|||
|
|
} else {
|
|||
|
|
command.execute();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取当前活动图层
|
|||
|
|
* @return {Object} layer 图层对象
|
|||
|
|
*/
|
|||
|
|
getActiveLayer() {
|
|||
|
|
// 查找当前活动图层
|
|||
|
|
const activeLayer = this.layers.value.find(
|
|||
|
|
(layer) => layer.id === this.activeLayerId.value
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
if (activeLayer) {
|
|||
|
|
return activeLayer;
|
|||
|
|
} else {
|
|||
|
|
console.warn("没有活动图层");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取当前活动图层ID
|
|||
|
|
getActiveLayerId() {
|
|||
|
|
return this.activeLayerId.value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 根据ID获取图层
|
|||
|
|
* @param {string} layerId 图层ID
|
|||
|
|
* @returns {Object|null} 图层对象或null
|
|||
|
|
*/
|
|||
|
|
getLayerById(layerId) {
|
|||
|
|
if (!layerId || !this.layers.value) return null;
|
|||
|
|
return this.layers.value.find((layer) => layer.id === layerId) || 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 = this.layers.value.find((layer) => layer.id === layerId);
|
|||
|
|
|
|||
|
|
// 如果是背景层或固定层,不允许删除
|
|||
|
|
if (layer && (layer.isBackground || layer.isFixed)) {
|
|||
|
|
console.warn(layer.isBackground ? "背景层不可删除" : "固定层不可删除");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果图层有子图层,提示确认
|
|||
|
|
if (layer && layer.children && layer.children.length > 0) {
|
|||
|
|
console.warn("该图层包含子图层,删除将同时删除所有子图层");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 直接创建和执行命令
|
|||
|
|
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();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 移动图层位置
|
|||
|
|
* @param {string} layerId 图层ID
|
|||
|
|
* @param {string} direction 移动方向,'up'或'down'
|
|||
|
|
* @returns {boolean} 是否移动成功
|
|||
|
|
*/
|
|||
|
|
moveLayer(layerId, direction) {
|
|||
|
|
// 查找要移动的图层
|
|||
|
|
const layer = this.layers.value.find((layer) => layer.id === layerId);
|
|||
|
|
|
|||
|
|
// 如果是背景层或固定层,不允许移动
|
|||
|
|
if (layer && (layer.isBackground || layer.isFixed)) {
|
|||
|
|
console.warn(layer.isBackground ? "背景层不可移动" : "固定层不可移动");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 直接创建和执行命令
|
|||
|
|
const command = new MoveLayerCommand({
|
|||
|
|
canvas: this.canvas,
|
|||
|
|
layers: this.layers,
|
|||
|
|
layerId: layerId,
|
|||
|
|
direction: direction,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 执行命令
|
|||
|
|
if (this.commandManager) {
|
|||
|
|
this.commandManager.execute(command);
|
|||
|
|
} else {
|
|||
|
|
command.execute();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新画布渲染顺序
|
|||
|
|
this._rearrangeObjects();
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 切换图层可见性
|
|||
|
|
* @param {string} layerId 图层ID
|
|||
|
|
* @returns {boolean} 更新后的可见性状态
|
|||
|
|
*/
|
|||
|
|
toggleLayerVisibility(layerId) {
|
|||
|
|
// 直接创建和执行命令
|
|||
|
|
const command = new ToggleLayerVisibilityCommand({
|
|||
|
|
canvas: this.canvas,
|
|||
|
|
layers: this.layers,
|
|||
|
|
layerId: layerId,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 执行命令
|
|||
|
|
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.visible : false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 切换图层锁定状态
|
|||
|
|
* @param {string} layerId 图层ID
|
|||
|
|
* @returns {boolean} 更新后的锁定状态
|
|||
|
|
*/
|
|||
|
|
toggleLayerLock(layerId) {
|
|||
|
|
// 直接创建和执行命令
|
|||
|
|
const command = new LayerLockCommand({
|
|||
|
|
canvas: this.canvas,
|
|||
|
|
layers: this.layers,
|
|||
|
|
layerId: layerId,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 执行命令
|
|||
|
|
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
|
|||
|
|
*/
|
|||
|
|
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,
|
|||
|
|
layerIds: layerIds,
|
|||
|
|
groupName: groupName,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 执行命令
|
|||
|
|
if (this.commandManager) {
|
|||
|
|
this.commandManager.execute(command);
|
|||
|
|
} else {
|
|||
|
|
command.execute();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return command.groupId;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 解组一个组图层
|
|||
|
|
* @param {string} groupId 要解组的组图层ID
|
|||
|
|
* @returns {Array<string>} 解组后的图层ID数组
|
|||
|
|
*/
|
|||
|
|
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,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 执行命令
|
|||
|
|
if (this.commandManager) {
|
|||
|
|
this.commandManager.execute(command);
|
|||
|
|
} else {
|
|||
|
|
command.execute();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return command.childLayerIds;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 调整画布和背景图层尺寸
|
|||
|
|
* @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 = !!options.undoable; // 设置为可撤销
|
|||
|
|
// 执行命令
|
|||
|
|
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.canvas) return;
|
|||
|
|
|
|||
|
|
// 获取画布上的所有对象
|
|||
|
|
const canvasObjects = [...this.canvas.getObjects()];
|
|||
|
|
|
|||
|
|
// 清空画布
|
|||
|
|
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) {
|
|||
|
|
// 背景图层特殊处理
|
|||
|
|
if (layer.fabricObject) {
|
|||
|
|
const bgObject = canvasObjects.find(
|
|||
|
|
(obj) => obj.id === layer.fabricObject.id
|
|||
|
|
);
|
|||
|
|
if (bgObject) {
|
|||
|
|
this.canvas.add(bgObject);
|
|||
|
|
} else if (layer.fabricObject) {
|
|||
|
|
this.canvas.add(layer.fabricObject);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} 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);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新对象交互性
|
|||
|
|
this.updateLayersObjectsInteractivity();
|
|||
|
|
|
|||
|
|
// 渲染画布
|
|||
|
|
this.canvas.renderAll();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 同步画布对象到图层数据
|
|||
|
|
* 确保画布上的对象和图层数据一致
|
|||
|
|
*/
|
|||
|
|
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,
|
|||
|
|
editorMode: this.editorMode,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 导入图层数据
|
|||
|
|
* @param {Object} data 图层数据对象
|
|||
|
|
* @returns {boolean} 是否导入成功
|
|||
|
|
*/
|
|||
|
|
importLayersData(data) {
|
|||
|
|
if (!data || !data.layers || !Array.isArray(data.layers)) {
|
|||
|
|
console.error("无效的图层数据");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const fabric = window.fabric;
|
|||
|
|
if (!fabric) {
|
|||
|
|
console.error("未找到fabric库");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 清除画布
|
|||
|
|
this.canvas.clear();
|
|||
|
|
|
|||
|
|
// 清空图层列表
|
|||
|
|
this.layers.value = [];
|
|||
|
|
|
|||
|
|
// 设置画布尺寸
|
|||
|
|
if (data.canvasWidth && data.canvasHeight) {
|
|||
|
|
this.canvasWidth = data.canvasWidth;
|
|||
|
|
this.canvasHeight = data.canvasHeight;
|
|||
|
|
this.canvas.setWidth(data.canvasWidth);
|
|||
|
|
this.canvas.setHeight(data.canvasHeight);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置背景颜色
|
|||
|
|
if (data.backgroundColor) {
|
|||
|
|
this.backgroundColor = data.backgroundColor;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置编辑模式
|
|||
|
|
if (data.editorMode) {
|
|||
|
|
this.editorMode = data.editorMode;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 导入图层
|
|||
|
|
const promises = data.layers.map((layerData) => {
|
|||
|
|
return new Promise((resolve) => {
|
|||
|
|
const newLayer = {
|
|||
|
|
...layerData,
|
|||
|
|
fabricObjects: [],
|
|||
|
|
children: layerData.children || [],
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 如果有序列化的对象,恢复它们
|
|||
|
|
if (
|
|||
|
|
layerData.serializedObjects &&
|
|||
|
|
Array.isArray(layerData.serializedObjects)
|
|||
|
|
) {
|
|||
|
|
fabric.util.enlivenObjects(layerData.serializedObjects, (objects) => {
|
|||
|
|
objects.forEach((obj) => {
|
|||
|
|
// 关联到图层
|
|||
|
|
obj.layerId = newLayer.id;
|
|||
|
|
obj.layerName = newLayer.name;
|
|||
|
|
|
|||
|
|
// 添加到画布
|
|||
|
|
this.canvas.add(obj);
|
|||
|
|
|
|||
|
|
// 添加到图层
|
|||
|
|
newLayer.fabricObjects.push(obj);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
resolve(newLayer);
|
|||
|
|
});
|
|||
|
|
} else if (
|
|||
|
|
layerData.isBackground &&
|
|||
|
|
layerData.serializedBackgroundObject
|
|||
|
|
) {
|
|||
|
|
// 恢复背景对象
|
|||
|
|
fabric.util.enlivenObjects(
|
|||
|
|
[layerData.serializedBackgroundObject],
|
|||
|
|
([bgObject]) => {
|
|||
|
|
if (bgObject) {
|
|||
|
|
bgObject.layerId = newLayer.id;
|
|||
|
|
bgObject.layerName = newLayer.name;
|
|||
|
|
this.canvas.add(bgObject);
|
|||
|
|
newLayer.fabricObject = bgObject;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
resolve(newLayer);
|
|||
|
|
}
|
|||
|
|
);
|
|||
|
|
} else {
|
|||
|
|
resolve(newLayer);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 等待所有图层加载完成
|
|||
|
|
Promise.all(promises).then((layers) => {
|
|||
|
|
// 更新图层列表
|
|||
|
|
this.layers.value = layers;
|
|||
|
|
|
|||
|
|
// 设置活动图层
|
|||
|
|
if (data.activeLayerId) {
|
|||
|
|
const activeLayer = layers.find(
|
|||
|
|
(layer) => layer.id === data.activeLayerId
|
|||
|
|
);
|
|||
|
|
if (activeLayer && !activeLayer.isBackground && !activeLayer.locked) {
|
|||
|
|
this.activeLayerId.value = data.activeLayerId;
|
|||
|
|
} else {
|
|||
|
|
// 查找第一个非背景、非锁定的图层
|
|||
|
|
const firstNormalLayer = layers.find(
|
|||
|
|
(layer) => !layer.isBackground && !layer.locked
|
|||
|
|
);
|
|||
|
|
if (firstNormalLayer) {
|
|||
|
|
this.activeLayerId.value = firstNormalLayer.id;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 确保至少有一个背景图层和一个普通图层
|
|||
|
|
this.initializeLayers();
|
|||
|
|
|
|||
|
|
// 更新对象交互性
|
|||
|
|
this.updateLayersObjectsInteractivity();
|
|||
|
|
|
|||
|
|
// 重新排列对象
|
|||
|
|
this._rearrangeObjects();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 复制图层数据到剪贴板
|
|||
|
|
* @param {string} layerId 要复制的图层ID
|
|||
|
|
* @returns {Object} 复制的图层数据
|
|||
|
|
*/
|
|||
|
|
copyLayer(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 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;
|
|||
|
|
|
|||
|
|
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);
|
|||
|
|
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
|
|||
|
|
*/
|
|||
|
|
pasteLayer() {
|
|||
|
|
if (!this.clipboardData) {
|
|||
|
|
console.error("剪贴板中没有图层数据");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 使用命令管理器执行粘贴命令
|
|||
|
|
if (this.commandManager) {
|
|||
|
|
return this.commandManager.executeByName("PasteLayer", {
|
|||
|
|
canvas: this.canvas,
|
|||
|
|
layers: this.layers,
|
|||
|
|
activeLayerId: this.activeLayerId,
|
|||
|
|
clipboardData: this.clipboardData,
|
|||
|
|
layerManager: this, // 传递LayerManager实例
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
// 创建粘贴图层命令
|
|||
|
|
const command = new PasteLayerCommand({
|
|||
|
|
canvas: this.canvas,
|
|||
|
|
layers: this.layers,
|
|||
|
|
activeLayerId: this.activeLayerId,
|
|||
|
|
clipboardData: this.clipboardData,
|
|||
|
|
layerManager: this, // 传递LayerManager实例
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 执行命令
|
|||
|
|
return 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,
|
|||
|
|
fabricImage,
|
|||
|
|
activeLayer,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 执行命令
|
|||
|
|
return command;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 更新背景图层颜色
|
|||
|
|
* @param {string} backgroundColor 背景颜色
|
|||
|
|
*/
|
|||
|
|
updateBackgroundColor(backgroundColor) {
|
|||
|
|
// 查找背景图层
|
|||
|
|
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,
|
|||
|
|
backgroundColor: backgroundColor,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 执行命令
|
|||
|
|
if (this.commandManager) {
|
|||
|
|
this.commandManager.execute(command);
|
|||
|
|
} else {
|
|||
|
|
command.execute();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新存储的背景颜色
|
|||
|
|
this.backgroundColor = backgroundColor;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取图层缩略图
|
|||
|
|
* @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} 创建的文本对象
|
|||
|
|
*/
|
|||
|
|
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 = 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);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置此图层为活动图层
|
|||
|
|
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} 是否排序成功
|
|||
|
|
*/
|
|||
|
|
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,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 执行命令
|
|||
|
|
if (this.commandManager) {
|
|||
|
|
return this.commandManager.execute(command);
|
|||
|
|
} else {
|
|||
|
|
return command.execute();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 拖拽排序子图层
|
|||
|
|
* @param {string} parentId 父图层ID
|
|||
|
|
* @param {number} oldIndex 原索引
|
|||
|
|
* @param {number} newIndex 新索引
|
|||
|
|
* @param {string} layerId 子图层ID
|
|||
|
|
* @returns {boolean} 是否排序成功
|
|||
|
|
*/
|
|||
|
|
reorderChildLayers(parentId, oldIndex, newIndex, layerId) {
|
|||
|
|
// 检查父图层是否存在
|
|||
|
|
const parentLayer = this.getLayerById(parentId);
|
|||
|
|
if (!parentLayer) {
|
|||
|
|
console.warn(`父图层 ${parentId} 不存在`);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取所有子图层
|
|||
|
|
const childLayers = this.layers.value.filter(
|
|||
|
|
(layer) => layer.parentId === parentId
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// 检查索引有效性
|
|||
|
|
if (
|
|||
|
|
oldIndex < 0 ||
|
|||
|
|
newIndex < 0 ||
|
|||
|
|
oldIndex >= childLayers.length ||
|
|||
|
|
newIndex >= childLayers.length
|
|||
|
|
) {
|
|||
|
|
console.warn("子图层排序索引无效");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查是否是同一位置
|
|||
|
|
if (oldIndex === newIndex) {
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证图层ID
|
|||
|
|
const layer = childLayers[oldIndex];
|
|||
|
|
if (!layer || layer.id !== layerId) {
|
|||
|
|
console.warn("子图层ID与索引不匹配");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建并执行子图层拖拽排序命令
|
|||
|
|
const command = new ReorderChildLayersCommand({
|
|||
|
|
layers: this.layers,
|
|||
|
|
parentId: parentId,
|
|||
|
|
oldIndex: oldIndex,
|
|||
|
|
newIndex: newIndex,
|
|||
|
|
layerId: layerId,
|
|||
|
|
canvas: this.canvas,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 执行命令
|
|||
|
|
if (this.commandManager) {
|
|||
|
|
return this.commandManager.execute(command);
|
|||
|
|
} else {
|
|||
|
|
return 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;
|
|||
|
|
}
|
|||
|
|
}
|