2802 lines
74 KiB
JavaScript
2802 lines
74 KiB
JavaScript
|
|
import { Command } from "./Command";
|
|||
|
|
import { createLayer, LayerType, OperationType } from "../utils/layerHelper";
|
|||
|
|
import { createStaticCanvas } from "../utils/canvasFactory";
|
|||
|
|
import { AddObjectToLayerCommand } from "./ObjectLayerCommands";
|
|||
|
|
import { ToolCommand } from "./ToolCommands";
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 添加图层命令
|
|||
|
|
*/
|
|||
|
|
export class AddLayerCommand extends Command {
|
|||
|
|
constructor(options) {
|
|||
|
|
super({
|
|||
|
|
name: "添加图层",
|
|||
|
|
saveState: true,
|
|||
|
|
});
|
|||
|
|
this.canvas = options.canvas;
|
|||
|
|
this.layers = options.layers;
|
|||
|
|
this.newLayer = options.newLayer;
|
|||
|
|
this.activeLayerId = options.activeLayerId;
|
|||
|
|
|
|||
|
|
this.insertIndex = options.insertIndex;
|
|||
|
|
this.oldActiveLayerId = null;
|
|||
|
|
this.beforeLayers = [...this.layers.value]; // 备份原图层列表
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
execute() {
|
|||
|
|
// 保存当前活动图层ID
|
|||
|
|
this.oldActiveLayerId = this.activeLayerId.value;
|
|||
|
|
|
|||
|
|
// 执行添加图层操作
|
|||
|
|
if (this.insertIndex !== undefined && this.insertIndex !== null) {
|
|||
|
|
this.layers.value.splice(this.insertIndex, 0, this.newLayer);
|
|||
|
|
} else {
|
|||
|
|
this.layers.value.push(this.newLayer);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新活动图层
|
|||
|
|
if (!this.newLayer.isBackground) {
|
|||
|
|
this.activeLayerId.value = this.newLayer.id;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return this.newLayer.id;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
undo() {
|
|||
|
|
// 从图层列表删除该图层
|
|||
|
|
this.layers.value = this.beforeLayers;
|
|||
|
|
|
|||
|
|
// 恢复原活动图层
|
|||
|
|
this.activeLayerId.value = this.oldActiveLayerId;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
getInfo() {
|
|||
|
|
return {
|
|||
|
|
name: this.name,
|
|||
|
|
layerName: this.newLayer.name,
|
|||
|
|
layerId: this.newLayer.id,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 粘贴并创建图层命令 - 更新为支持异步操作
|
|||
|
|
*/
|
|||
|
|
export class PasteLayerCommand extends Command {
|
|||
|
|
constructor(options) {
|
|||
|
|
super({
|
|||
|
|
name: "粘贴图层",
|
|||
|
|
saveState: true,
|
|||
|
|
});
|
|||
|
|
this.canvas = options.canvas;
|
|||
|
|
this.layers = options.layers;
|
|||
|
|
this.activeLayerId = options.activeLayerId;
|
|||
|
|
this.clipboardData = options.clipboardData;
|
|||
|
|
this.layerManager = options.layerManager;
|
|||
|
|
|
|||
|
|
// 新图层相关属性
|
|||
|
|
this.newLayer = null;
|
|||
|
|
this.newLayerId = null;
|
|||
|
|
this.insertIndex = null;
|
|||
|
|
this.oldActiveLayerId = null;
|
|||
|
|
this.createdObjects = [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async execute() {
|
|||
|
|
if (!this.clipboardData) {
|
|||
|
|
console.error("剪贴板中没有图层数据");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const data = this.clipboardData;
|
|||
|
|
const fabric = window.fabric;
|
|||
|
|
|
|||
|
|
if (!fabric) {
|
|||
|
|
console.error("未找到fabric库");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 生成新的图层ID
|
|||
|
|
this.newLayerId = `layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
|
|||
|
|
|
|||
|
|
// 创建新图层
|
|||
|
|
this.newLayer = {
|
|||
|
|
...data,
|
|||
|
|
id: this.newLayerId,
|
|||
|
|
name: `${data.name} 副本`,
|
|||
|
|
fabricObjects: [],
|
|||
|
|
isCut: undefined,
|
|||
|
|
serializedObjects: undefined,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 保存当前活动图层ID
|
|||
|
|
this.oldActiveLayerId = this.activeLayerId.value;
|
|||
|
|
|
|||
|
|
// 计算插入位置
|
|||
|
|
this.insertIndex = this.layerManager._getInsertIndexAboveActiveLayer();
|
|||
|
|
|
|||
|
|
// 执行添加图层操作
|
|||
|
|
if (this.insertIndex !== undefined && this.insertIndex !== null) {
|
|||
|
|
this.layers.value.splice(this.insertIndex, 0, this.newLayer);
|
|||
|
|
} else {
|
|||
|
|
this.layers.value.push(this.newLayer);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新活动图层
|
|||
|
|
if (!this.newLayer.isBackground) {
|
|||
|
|
this.activeLayerId.value = this.newLayer.id;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果有序列化的对象,异步恢复它们
|
|||
|
|
if (
|
|||
|
|
data.serializedObjects &&
|
|||
|
|
Array.isArray(data.serializedObjects) &&
|
|||
|
|
data.serializedObjects.length > 0
|
|||
|
|
) {
|
|||
|
|
await this._restoreObjectsAsync(data);
|
|||
|
|
} else {
|
|||
|
|
this._onObjectsRestored(data);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return this.newLayerId;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 异步恢复序列化的对象
|
|||
|
|
* @param {Object} data 剪贴板数据
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
async _restoreObjectsAsync(data) {
|
|||
|
|
const fabric = window.fabric;
|
|||
|
|
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
fabric.util.enlivenObjects(data.serializedObjects, (objects) => {
|
|||
|
|
try {
|
|||
|
|
objects.forEach((obj) => {
|
|||
|
|
// 生成新的对象ID
|
|||
|
|
const newObjId = `obj_${Date.now()}_${Math.floor(
|
|||
|
|
Math.random() * 1000
|
|||
|
|
)}`;
|
|||
|
|
obj.id = newObjId;
|
|||
|
|
obj.layerId = this.newLayerId;
|
|||
|
|
obj.layerName = this.newLayer.name;
|
|||
|
|
|
|||
|
|
// 如果是复制操作,给对象添加偏移量
|
|||
|
|
if (!data.isCut) {
|
|||
|
|
const offset = 10;
|
|||
|
|
if (obj.left !== undefined) obj.left += offset;
|
|||
|
|
if (obj.top !== undefined) obj.top += offset;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加到画布
|
|||
|
|
this.canvas.add(obj);
|
|||
|
|
|
|||
|
|
// 添加到图层
|
|||
|
|
this.newLayer.fabricObjects.push(obj);
|
|||
|
|
|
|||
|
|
// 记录创建的对象,用于撤销
|
|||
|
|
this.createdObjects.push(obj);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
this._onObjectsRestored(data);
|
|||
|
|
resolve();
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error("恢复对象时发生错误:", error);
|
|||
|
|
reject(error);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 对象恢复完成后的处理
|
|||
|
|
* @param {Object} data 剪贴板数据
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
_onObjectsRestored(data) {
|
|||
|
|
// 更新对象交互性
|
|||
|
|
this.layerManager?.updateLayersObjectsInteractivity?.();
|
|||
|
|
|
|||
|
|
// 重新排列对象
|
|||
|
|
this.layerManager?._rearrangeObjects?.();
|
|||
|
|
|
|||
|
|
// 判断如果是剪切操作,粘贴完后需要删除剪贴板数据
|
|||
|
|
if (data.isCut && this.layerManager) {
|
|||
|
|
this.layerManager.clipboardData = null;
|
|||
|
|
console.log(`已粘贴图层:${this.newLayer.name}(剪切)`);
|
|||
|
|
} else {
|
|||
|
|
console.log(`已粘贴图层:${this.newLayer.name}(复制)`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 重新渲染画布
|
|||
|
|
if (this.canvas) {
|
|||
|
|
this.canvas.renderAll();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
undo() {
|
|||
|
|
if (!this.newLayer || !this.newLayerId) return;
|
|||
|
|
|
|||
|
|
// 从图层列表删除该图层
|
|||
|
|
const index = this.layers.value.findIndex(
|
|||
|
|
(layer) => layer.id === this.newLayerId
|
|||
|
|
);
|
|||
|
|
if (index !== -1) {
|
|||
|
|
this.layers.value.splice(index, 1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 恢复原活动图层
|
|||
|
|
this.activeLayerId.value = this.oldActiveLayerId;
|
|||
|
|
|
|||
|
|
// 从画布移除所有创建的对象
|
|||
|
|
this.createdObjects.forEach((obj) => {
|
|||
|
|
this.canvas.remove(obj);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 如果图层有其他fabric对象,也要移除
|
|||
|
|
if (this.newLayer.fabricObjects && this.newLayer.fabricObjects.length > 0) {
|
|||
|
|
this.newLayer.fabricObjects.forEach((obj) => {
|
|||
|
|
if (!this.createdObjects.includes(obj)) {
|
|||
|
|
this.canvas.remove(obj);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果是剪切操作的撤销,需要恢复剪贴板数据
|
|||
|
|
if (this.clipboardData && this.clipboardData.isCut && this.layerManager) {
|
|||
|
|
this.layerManager.clipboardData = this.clipboardData;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 重新渲染画布
|
|||
|
|
if (this.canvas) {
|
|||
|
|
this.canvas.renderAll();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新对象交互性
|
|||
|
|
if (
|
|||
|
|
this.layerManager &&
|
|||
|
|
typeof this.layerManager.updateLayersObjectsInteractivity === "function"
|
|||
|
|
) {
|
|||
|
|
this.layerManager.updateLayersObjectsInteractivity();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
getInfo() {
|
|||
|
|
return {
|
|||
|
|
name: this.name,
|
|||
|
|
layerName: this.newLayer?.name || "未知图层",
|
|||
|
|
layerId: this.newLayerId,
|
|||
|
|
objectCount: this.createdObjects.length,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 移除图层命令
|
|||
|
|
*/
|
|||
|
|
export class RemoveLayerCommand extends Command {
|
|||
|
|
constructor(options) {
|
|||
|
|
super({
|
|||
|
|
name: "移除图层",
|
|||
|
|
saveState: true,
|
|||
|
|
});
|
|||
|
|
this.canvas = options.canvas;
|
|||
|
|
this.layers = options.layers;
|
|||
|
|
this.layerId = options.layerId;
|
|||
|
|
this.activeLayerId = options.activeLayerId;
|
|||
|
|
|
|||
|
|
// 查找要删除的图层
|
|||
|
|
this.layerIndex = this.layers.value.findIndex(
|
|||
|
|
(layer) => layer.id === this.layerId
|
|||
|
|
);
|
|||
|
|
this.removedLayer = this.layers.value[this.layerIndex];
|
|||
|
|
this.isActiveLayer = this.layerId === this.activeLayerId.value;
|
|||
|
|
// this.beforeLayers = [...this.layers.value]; // 备份原图层列表
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
execute() {
|
|||
|
|
if (this.layerIndex === -1 || !this.removedLayer) {
|
|||
|
|
console.error(`图层 ${this.layerId} 不存在`);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 从画布中移除图层中的所有对象
|
|||
|
|
if (
|
|||
|
|
this.removedLayer.fabricObjects &&
|
|||
|
|
this.removedLayer.fabricObjects.length > 0
|
|||
|
|
) {
|
|||
|
|
this.removedLayer.fabricObjects.forEach((obj) => {
|
|||
|
|
this.canvas.remove(obj);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果是背景图层,移除特殊对象
|
|||
|
|
if (this.removedLayer.isBackground && this.removedLayer.fabricObject) {
|
|||
|
|
this.canvas.remove(this.removedLayer.fabricObject);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 从图层列表中删除
|
|||
|
|
this.layers.value.splice(this.layerIndex, 1);
|
|||
|
|
|
|||
|
|
// 如果删除的是当前活动图层,需要更新活动图层
|
|||
|
|
if (this.isActiveLayer) {
|
|||
|
|
// 查找最近的非背景层作为新的活动图层
|
|||
|
|
const newActiveLayer = this.layers.value.find(
|
|||
|
|
(layer) => !layer.isBackground
|
|||
|
|
);
|
|||
|
|
if (newActiveLayer) {
|
|||
|
|
this.activeLayerId.value = newActiveLayer.id;
|
|||
|
|
} else {
|
|||
|
|
this.activeLayerId.value = null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 重新渲染画布
|
|||
|
|
if (this.canvas) {
|
|||
|
|
this.canvas.renderAll();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
undo() {
|
|||
|
|
// 恢复图层
|
|||
|
|
if (this.layerIndex !== -1 && this.removedLayer) {
|
|||
|
|
this.layers.value.splice(this.layerIndex, 0, this.removedLayer);
|
|||
|
|
|
|||
|
|
// 恢复图层中的所有对象到画布
|
|||
|
|
if (
|
|||
|
|
this.removedLayer.fabricObjects &&
|
|||
|
|
this.removedLayer.fabricObjects.length > 0
|
|||
|
|
) {
|
|||
|
|
this.removedLayer.fabricObjects.forEach((obj) => {
|
|||
|
|
this.canvas.add(obj);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果是背景图层,恢复特殊对象
|
|||
|
|
if (this.removedLayer.isBackground && this.removedLayer.fabricObject) {
|
|||
|
|
this.canvas.add(this.removedLayer.fabricObject);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果删除的是当前活动图层,恢复活动图层
|
|||
|
|
if (this.isActiveLayer) {
|
|||
|
|
this.activeLayerId.value = this.layerId;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 重新渲染画布
|
|||
|
|
if (this.canvas) {
|
|||
|
|
this.canvas.renderAll();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
getInfo() {
|
|||
|
|
return {
|
|||
|
|
name: this.name,
|
|||
|
|
layerName: this.removedLayer?.name || "未知图层",
|
|||
|
|
layerId: this.layerId,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 移动图层命令
|
|||
|
|
*/
|
|||
|
|
export class MoveLayerCommand extends Command {
|
|||
|
|
constructor(options) {
|
|||
|
|
super({
|
|||
|
|
name: `移动图层 ${options.direction === "up" ? "上移" : "下移"}`,
|
|||
|
|
saveState: false,
|
|||
|
|
});
|
|||
|
|
this.canvas = options.canvas;
|
|||
|
|
this.layers = options.layers;
|
|||
|
|
this.layerId = options.layerId;
|
|||
|
|
this.direction = options.direction; // "up" or "down"
|
|||
|
|
|
|||
|
|
// 查找图层索引
|
|||
|
|
this.layerIndex = this.layers.value.findIndex(
|
|||
|
|
(layer) => layer.id === this.layerId
|
|||
|
|
);
|
|||
|
|
// this.beforeLayers = [...this.layers.value]; // 备份原图层列表
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
execute() {
|
|||
|
|
if (this.layerIndex === -1) {
|
|||
|
|
console.error(`图层 ${this.layerId} 不存在`);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const layer = this.layers.value[this.layerIndex];
|
|||
|
|
let newIndex = this.layerIndex;
|
|||
|
|
|
|||
|
|
// 计算新索引
|
|||
|
|
if (this.direction === "up") {
|
|||
|
|
// 向上移动(在数组中向前移动)
|
|||
|
|
if (this.layerIndex > 0) {
|
|||
|
|
// 确保不会移动到背景层之前
|
|||
|
|
const prevLayer = this.layers.value[this.layerIndex - 1];
|
|||
|
|
if (!prevLayer.isBackground) {
|
|||
|
|
newIndex = this.layerIndex - 1;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else if (this.direction === "down") {
|
|||
|
|
// 向下移动(在数组中向后移动)
|
|||
|
|
if (this.layerIndex < this.layers.value.length - 1) {
|
|||
|
|
// 确保不会移动背景层
|
|||
|
|
if (!layer.isBackground) {
|
|||
|
|
newIndex = this.layerIndex + 1;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果位置有变化,执行移动
|
|||
|
|
if (newIndex !== this.layerIndex) {
|
|||
|
|
// 移除原位置
|
|||
|
|
this.layers.value.splice(this.layerIndex, 1);
|
|||
|
|
// 插入到新位置
|
|||
|
|
this.layers.value.splice(newIndex, 0, layer);
|
|||
|
|
this.newIndex = newIndex;
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
undo() {
|
|||
|
|
if (this.layerIndex !== this.newIndex && this.newIndex !== undefined) {
|
|||
|
|
// 获取图层
|
|||
|
|
const layer = this.layers.value[this.newIndex];
|
|||
|
|
|
|||
|
|
// 移除新位置
|
|||
|
|
this.layers.value.splice(this.newIndex, 1);
|
|||
|
|
|
|||
|
|
// 插入到原位置
|
|||
|
|
this.layers.value.splice(this.layerIndex, 0, layer);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
getInfo() {
|
|||
|
|
return {
|
|||
|
|
name: this.name,
|
|||
|
|
layerId: this.layerId,
|
|||
|
|
direction: this.direction,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 切换图层可见性命令
|
|||
|
|
*/
|
|||
|
|
export class ToggleLayerVisibilityCommand extends Command {
|
|||
|
|
constructor(options) {
|
|||
|
|
super({
|
|||
|
|
name: "切换图层可见性",
|
|||
|
|
saveState: false,
|
|||
|
|
});
|
|||
|
|
this.canvas = options.canvas;
|
|||
|
|
this.layers = options.layers;
|
|||
|
|
this.layerId = options.layerId;
|
|||
|
|
|
|||
|
|
// 查找图层
|
|||
|
|
this.layer = this.layers.value.find((layer) => layer.id === this.layerId);
|
|||
|
|
this.oldVisibility = this.layer ? this.layer.visible : null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
execute() {
|
|||
|
|
if (!this.layer) {
|
|||
|
|
console.error(`图层 ${this.layerId} 不存在`);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 切换可见性
|
|||
|
|
this.layer.visible = !this.oldVisibility;
|
|||
|
|
|
|||
|
|
// 更新画布上图层对象的可见性
|
|||
|
|
if (this.canvas) {
|
|||
|
|
const layerObjects = this.canvas
|
|||
|
|
.getObjects()
|
|||
|
|
.filter((obj) => obj.layerId === this.layerId);
|
|||
|
|
|
|||
|
|
layerObjects.forEach((obj) => {
|
|||
|
|
obj.visible = this.layer.visible;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
this.canvas.renderAll();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
undo() {
|
|||
|
|
if (this.layer) {
|
|||
|
|
// 恢复可见性
|
|||
|
|
this.layer.visible = this.oldVisibility;
|
|||
|
|
|
|||
|
|
// 更新画布上图层对象的可见性
|
|||
|
|
if (this.canvas) {
|
|||
|
|
const layerObjects = this.canvas
|
|||
|
|
.getObjects()
|
|||
|
|
.filter((obj) => obj.layerId === this.layerId);
|
|||
|
|
|
|||
|
|
layerObjects.forEach((obj) => {
|
|||
|
|
obj.visible = this.oldVisibility;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
this.canvas.renderAll();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
getInfo() {
|
|||
|
|
return {
|
|||
|
|
name: this.name,
|
|||
|
|
layerName: this.layer?.name || "未知图层",
|
|||
|
|
layerId: this.layerId,
|
|||
|
|
newVisibility: this.layer?.visible,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 重命名图层命令
|
|||
|
|
*/
|
|||
|
|
export class RenameLayerCommand extends Command {
|
|||
|
|
constructor(options) {
|
|||
|
|
super({
|
|||
|
|
name: "重命名图层",
|
|||
|
|
saveState: false,
|
|||
|
|
});
|
|||
|
|
this.canvas = options.canvas;
|
|||
|
|
this.layers = options.layers;
|
|||
|
|
this.layerId = options.layerId;
|
|||
|
|
this.newName = options.newName;
|
|||
|
|
|
|||
|
|
// 查找图层
|
|||
|
|
this.layer = this.layers.value.find((layer) => layer.id === this.layerId);
|
|||
|
|
this.oldName = this.layer ? this.layer.name : null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
execute() {
|
|||
|
|
if (!this.layer) {
|
|||
|
|
console.error(`图层 ${this.layerId} 不存在`);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新图层名称
|
|||
|
|
this.layer.name = this.newName;
|
|||
|
|
|
|||
|
|
// 更新图层对象上的图层名称
|
|||
|
|
if (this.canvas) {
|
|||
|
|
const layerObjects = this.canvas
|
|||
|
|
.getObjects()
|
|||
|
|
.filter((obj) => obj.layerId === this.layerId);
|
|||
|
|
|
|||
|
|
layerObjects.forEach((obj) => {
|
|||
|
|
obj.layerName = this.newName;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
undo() {
|
|||
|
|
if (this.layer && this.oldName) {
|
|||
|
|
// 恢复图层名称
|
|||
|
|
this.layer.name = this.oldName;
|
|||
|
|
|
|||
|
|
// 恢复图层对象上的图层名称
|
|||
|
|
if (this.canvas) {
|
|||
|
|
const layerObjects = this.canvas
|
|||
|
|
.getObjects()
|
|||
|
|
.filter((obj) => obj.layerId === this.layerId);
|
|||
|
|
|
|||
|
|
layerObjects.forEach((obj) => {
|
|||
|
|
obj.layerName = this.oldName;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
getInfo() {
|
|||
|
|
return {
|
|||
|
|
name: this.name,
|
|||
|
|
layerId: this.layerId,
|
|||
|
|
oldName: this.oldName,
|
|||
|
|
newName: this.newName,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 图层锁定/解锁命令
|
|||
|
|
*/
|
|||
|
|
export class LayerLockCommand extends Command {
|
|||
|
|
constructor(options) {
|
|||
|
|
super({
|
|||
|
|
name: "图层锁定/解锁",
|
|||
|
|
saveState: false,
|
|||
|
|
});
|
|||
|
|
this.canvas = options.canvas;
|
|||
|
|
this.layers = options.layers;
|
|||
|
|
this.layerId = options.layerId;
|
|||
|
|
|
|||
|
|
// 查找图层
|
|||
|
|
this.layer = this.layers.value.find((layer) => layer.id === this.layerId);
|
|||
|
|
this.oldLocked = this.layer ? this.layer.locked : null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
execute() {
|
|||
|
|
if (!this.layer) {
|
|||
|
|
console.error(`图层 ${this.layerId} 不存在`);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 切换锁定状态
|
|||
|
|
this.layer.locked = !this.oldLocked;
|
|||
|
|
|
|||
|
|
// 更新画布上对象的可选择状态
|
|||
|
|
if (this.canvas) {
|
|||
|
|
const layerObjects = this.canvas
|
|||
|
|
.getObjects()
|
|||
|
|
.filter((obj) => obj.layerId === this.layerId);
|
|||
|
|
|
|||
|
|
layerObjects.forEach((obj) => {
|
|||
|
|
obj.selectable = !this.layer.locked && this.layer.visible;
|
|||
|
|
obj.evented = !this.layer.locked && this.layer.visible;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
this.canvas.renderAll();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
undo() {
|
|||
|
|
if (this.layer) {
|
|||
|
|
// 恢复锁定状态
|
|||
|
|
this.layer.locked = this.oldLocked;
|
|||
|
|
|
|||
|
|
// 更新画布上对象的可选择状态
|
|||
|
|
if (this.canvas) {
|
|||
|
|
const layerObjects = this.canvas
|
|||
|
|
.getObjects()
|
|||
|
|
.filter((obj) => obj.layerId === this.layerId);
|
|||
|
|
|
|||
|
|
layerObjects.forEach((obj) => {
|
|||
|
|
obj.selectable = !this.oldLocked && this.layer.visible;
|
|||
|
|
obj.evented = !this.oldLocked && this.layer.visible;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
this.canvas.renderAll();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
getInfo() {
|
|||
|
|
return {
|
|||
|
|
name: this.name,
|
|||
|
|
layerName: this.layer?.name || "未知图层",
|
|||
|
|
layerId: this.layerId,
|
|||
|
|
newLocked: this.layer?.locked,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 设置图层不透明度命令
|
|||
|
|
*/
|
|||
|
|
export class SetLayerOpacityCommand extends Command {
|
|||
|
|
constructor(options) {
|
|||
|
|
super({
|
|||
|
|
name: "设置图层不透明度",
|
|||
|
|
saveState: false,
|
|||
|
|
});
|
|||
|
|
this.canvas = options.canvas;
|
|||
|
|
this.layers = options.layers;
|
|||
|
|
this.layerId = options.layerId;
|
|||
|
|
this.opacity = options.opacity;
|
|||
|
|
|
|||
|
|
// 查找图层
|
|||
|
|
this.layer = this.layers.value.find((layer) => layer.id === this.layerId);
|
|||
|
|
this.oldOpacity = this.layer ? this.layer.opacity : null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
execute() {
|
|||
|
|
if (!this.layer) {
|
|||
|
|
console.error(`图层 ${this.layerId} 不存在`);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置图层不透明度
|
|||
|
|
this.layer.opacity = this.opacity;
|
|||
|
|
|
|||
|
|
// 更新画布上对象的不透明度
|
|||
|
|
if (this.canvas) {
|
|||
|
|
const layerObjects = this.canvas
|
|||
|
|
.getObjects()
|
|||
|
|
.filter((obj) => obj.layerId === this.layerId);
|
|||
|
|
|
|||
|
|
layerObjects.forEach((obj) => {
|
|||
|
|
obj.opacity = this.opacity;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
this.canvas.renderAll();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
undo() {
|
|||
|
|
if (this.layer && this.oldOpacity !== null) {
|
|||
|
|
// 恢复图层不透明度
|
|||
|
|
this.layer.opacity = this.oldOpacity;
|
|||
|
|
|
|||
|
|
// 更新画布上对象的不透明度
|
|||
|
|
if (this.canvas) {
|
|||
|
|
const layerObjects = this.canvas
|
|||
|
|
.getObjects()
|
|||
|
|
.filter((obj) => obj.layerId === this.layerId);
|
|||
|
|
|
|||
|
|
layerObjects.forEach((obj) => {
|
|||
|
|
obj.opacity = this.oldOpacity;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
this.canvas.renderAll();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
getInfo() {
|
|||
|
|
return {
|
|||
|
|
name: this.name,
|
|||
|
|
layerName: this.layer?.name || "未知图层",
|
|||
|
|
layerId: this.layerId,
|
|||
|
|
oldOpacity: this.oldOpacity,
|
|||
|
|
newOpacity: this.opacity,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 设置图层混合模式命令
|
|||
|
|
*/
|
|||
|
|
export class SetLayerBlendModeCommand extends Command {
|
|||
|
|
constructor(options) {
|
|||
|
|
super({
|
|||
|
|
name: "设置图层混合模式",
|
|||
|
|
saveState: false,
|
|||
|
|
});
|
|||
|
|
this.canvas = options.canvas;
|
|||
|
|
this.layers = options.layers;
|
|||
|
|
this.layerId = options.layerId;
|
|||
|
|
this.blendMode = options.blendMode;
|
|||
|
|
|
|||
|
|
// 查找图层
|
|||
|
|
this.layer = this.layers.value.find((layer) => layer.id === this.layerId);
|
|||
|
|
this.oldBlendMode = this.layer ? this.layer.blendMode : null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
execute() {
|
|||
|
|
if (!this.layer) {
|
|||
|
|
console.error(`图层 ${this.layerId} 不存在`);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置图层混合模式
|
|||
|
|
this.layer.blendMode = this.blendMode;
|
|||
|
|
|
|||
|
|
// 更新画布上对象的混合模式
|
|||
|
|
if (this.canvas) {
|
|||
|
|
const layerObjects = this.canvas
|
|||
|
|
.getObjects()
|
|||
|
|
.filter((obj) => obj.layerId === this.layerId);
|
|||
|
|
|
|||
|
|
layerObjects.forEach((obj) => {
|
|||
|
|
obj.globalCompositeOperation = this.blendMode;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
this.canvas.renderAll();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
undo() {
|
|||
|
|
if (this.layer && this.oldBlendMode) {
|
|||
|
|
// 恢复图层混合模式
|
|||
|
|
this.layer.blendMode = this.oldBlendMode;
|
|||
|
|
|
|||
|
|
// 更新画布上对象的混合模式
|
|||
|
|
if (this.canvas) {
|
|||
|
|
const layerObjects = this.canvas
|
|||
|
|
.getObjects()
|
|||
|
|
.filter((obj) => obj.layerId === this.layerId);
|
|||
|
|
|
|||
|
|
layerObjects.forEach((obj) => {
|
|||
|
|
obj.globalCompositeOperation = this.oldBlendMode;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
this.canvas.renderAll();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
getInfo() {
|
|||
|
|
return {
|
|||
|
|
name: this.name,
|
|||
|
|
layerName: this.layer?.name || "未知图层",
|
|||
|
|
layerId: this.layerId,
|
|||
|
|
oldBlendMode: this.oldBlendMode,
|
|||
|
|
newBlendMode: this.blendMode,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 合并图层命令
|
|||
|
|
*/
|
|||
|
|
export class MergeLayersCommand extends Command {
|
|||
|
|
constructor(options) {
|
|||
|
|
super({
|
|||
|
|
name: "合并图层",
|
|||
|
|
saveState: true,
|
|||
|
|
});
|
|||
|
|
this.canvas = options.canvas;
|
|||
|
|
this.layers = options.layers;
|
|||
|
|
this.layerIds = options.layerIds;
|
|||
|
|
this.newName = options.newName;
|
|||
|
|
this.activeLayerId = options.activeLayerId; // <--- 新增
|
|||
|
|
|
|||
|
|
// 备份原图层
|
|||
|
|
this.originalLayers = [...this.layers.value];
|
|||
|
|
// 新图层ID
|
|||
|
|
this.newLayerId = `merged_layer_${Date.now()}_${Math.floor(
|
|||
|
|
Math.random() * 1000
|
|||
|
|
)}`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
execute() {
|
|||
|
|
if (
|
|||
|
|
!this.layerIds ||
|
|||
|
|
!Array.isArray(this.layerIds) ||
|
|||
|
|
this.layerIds.length < 2
|
|||
|
|
) {
|
|||
|
|
console.error("合并图层至少需要两个图层");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 查找所有要合并的图层
|
|||
|
|
const layersToMerge = this.layerIds
|
|||
|
|
.map((id) => this.layers.value.find((layer) => layer.id === id))
|
|||
|
|
.filter(Boolean);
|
|||
|
|
|
|||
|
|
if (layersToMerge.length < 2) {
|
|||
|
|
console.error("找不到足够的图层进行合并");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查是否包含背景图层
|
|||
|
|
if (layersToMerge.some((layer) => layer.isBackground)) {
|
|||
|
|
console.error("不能合并背景图层");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 查找最顶层图层的索引,用于插入合并后的图层
|
|||
|
|
const topLayerIndex = Math.min(
|
|||
|
|
...layersToMerge.map((layer) => this.layers.value.indexOf(layer))
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// 获取要保留的所有对象
|
|||
|
|
const allObjects = [];
|
|||
|
|
layersToMerge.forEach((layer) => {
|
|||
|
|
if (Array.isArray(layer.fabricObjects)) {
|
|||
|
|
allObjects.push(...layer.fabricObjects);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 创建新的合并图层
|
|||
|
|
const mergedLayer = createLayer({
|
|||
|
|
id: this.newLayerId,
|
|||
|
|
name: this.newName || `合并图层`,
|
|||
|
|
type: LayerType.BITMAP,
|
|||
|
|
visible: true,
|
|||
|
|
locked: false,
|
|||
|
|
opacity: 1.0,
|
|||
|
|
fabricObjects: allObjects,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 更新对象与新图层的关联
|
|||
|
|
allObjects.forEach((obj) => {
|
|||
|
|
obj.layerId = mergedLayer.id;
|
|||
|
|
obj.layerName = mergedLayer.name;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 移除原图层
|
|||
|
|
this.layers.value = this.layers.value.filter(
|
|||
|
|
(layer) => !this.layerIds.includes(layer.id)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// 插入新图层
|
|||
|
|
this.layers.value.splice(topLayerIndex, 0, mergedLayer);
|
|||
|
|
|
|||
|
|
// 更新当前活动图层
|
|||
|
|
this.activeLayerId.value = this.newLayerId;
|
|||
|
|
|
|||
|
|
// 重新渲染画布
|
|||
|
|
if (this.canvas) {
|
|||
|
|
this.canvas.renderAll();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return this.newLayerId;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
undo() {
|
|||
|
|
// 恢复原始图层状态
|
|||
|
|
this.layers.value = [...this.originalLayers];
|
|||
|
|
|
|||
|
|
// 恢复活动图层
|
|||
|
|
if (this.activeLayerId) {
|
|||
|
|
// 恢复到合并前的活动图层(取第一个合并前图层)
|
|||
|
|
this.activeLayerId.value = this.layerIds[0];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 重新渲染画布
|
|||
|
|
if (this.canvas) {
|
|||
|
|
this.canvas.renderAll();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
getInfo() {
|
|||
|
|
return {
|
|||
|
|
name: this.name,
|
|||
|
|
layerIds: this.layerIds,
|
|||
|
|
newLayerId: this.newLayerId,
|
|||
|
|
newName: this.newName,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 图层组合命令
|
|||
|
|
*/
|
|||
|
|
export class GroupLayersCommand extends Command {
|
|||
|
|
constructor(options) {
|
|||
|
|
super({
|
|||
|
|
name: "组合图层",
|
|||
|
|
saveState: true,
|
|||
|
|
});
|
|||
|
|
this.canvas = options.canvas;
|
|||
|
|
this.layers = options.layers;
|
|||
|
|
this.layerIds = options.layerIds;
|
|||
|
|
this.groupName = options.groupName;
|
|||
|
|
this.activeLayerId = options.activeLayerId; // <--- 新增
|
|||
|
|
|
|||
|
|
// 备份原图层
|
|||
|
|
this.originalLayers = [...this.layers.value];
|
|||
|
|
// 新组ID
|
|||
|
|
this.groupId = `group_layer_${Date.now()}_${Math.floor(
|
|||
|
|
Math.random() * 1000
|
|||
|
|
)}`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
execute() {
|
|||
|
|
if (
|
|||
|
|
!this.layerIds ||
|
|||
|
|
!Array.isArray(this.layerIds) ||
|
|||
|
|
this.layerIds.length < 2
|
|||
|
|
) {
|
|||
|
|
console.error("组合图层至少需要两个图层");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 查找所有要组合的图层
|
|||
|
|
const layersToGroup = this.layerIds
|
|||
|
|
.map((id) => this.layers.value.find((layer) => layer.id === id))
|
|||
|
|
.filter(Boolean);
|
|||
|
|
|
|||
|
|
if (layersToGroup.length < 2) {
|
|||
|
|
console.error("找不到足够的图层进行组合");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查是否包含背景图层
|
|||
|
|
if (layersToGroup.some((layer) => layer.isBackground)) {
|
|||
|
|
console.error("不能组合背景图层");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 查找最顶层图层的索引,用于插入组图层
|
|||
|
|
const topLayerIndex = Math.min(
|
|||
|
|
...layersToGroup.map((layer) => this.layers.value.indexOf(layer))
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// 创建新的组图层
|
|||
|
|
const groupLayer = createLayer({
|
|||
|
|
id: this.groupId,
|
|||
|
|
name: this.groupName || `图层组`,
|
|||
|
|
type: LayerType.GROUP,
|
|||
|
|
visible: true,
|
|||
|
|
locked: false,
|
|||
|
|
opacity: 1.0,
|
|||
|
|
fabricObjects: [],
|
|||
|
|
children: layersToGroup,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 移除原图层
|
|||
|
|
this.layers.value = this.layers.value.filter(
|
|||
|
|
(layer) => !this.layerIds.includes(layer.id)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// 插入新组图层
|
|||
|
|
this.layers.value.splice(topLayerIndex, 0, groupLayer);
|
|||
|
|
|
|||
|
|
// 更新当前活动图层
|
|||
|
|
this.activeLayerId.value = this.groupId;
|
|||
|
|
|
|||
|
|
return this.groupId;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
undo() {
|
|||
|
|
// 恢复原始图层状态
|
|||
|
|
this.layers.value = [...this.originalLayers];
|
|||
|
|
// 恢复活动图层为原先第一个合并前层
|
|||
|
|
if (this.activeLayerId) {
|
|||
|
|
this.activeLayerId.value = this.layerIds[0];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
getInfo() {
|
|||
|
|
return {
|
|||
|
|
name: this.name,
|
|||
|
|
layerIds: this.layerIds,
|
|||
|
|
groupId: this.groupId,
|
|||
|
|
groupName: this.groupName,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 解组图层命令
|
|||
|
|
*/
|
|||
|
|
export class UngroupLayersCommand extends Command {
|
|||
|
|
constructor(options) {
|
|||
|
|
super({
|
|||
|
|
name: "解组图层",
|
|||
|
|
saveState: true,
|
|||
|
|
});
|
|||
|
|
this.canvas = options.canvas;
|
|||
|
|
this.layers = options.layers;
|
|||
|
|
this.groupId = options.groupId;
|
|||
|
|
this.activeLayerId = options.activeLayerId; // <--- 新增
|
|||
|
|
|
|||
|
|
// 备份原图层
|
|||
|
|
this.originalLayers = [...this.layers.value];
|
|||
|
|
// 子图层ID列表
|
|||
|
|
this.childLayerIds = [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
execute() {
|
|||
|
|
// 查找组图层
|
|||
|
|
const groupIndex = this.layers.value.findIndex(
|
|||
|
|
(layer) => layer.id === this.groupId
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
if (groupIndex === -1) {
|
|||
|
|
console.error(`找不到组图层 ${this.groupId}`);
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const groupLayer = this.layers.value[groupIndex];
|
|||
|
|
|
|||
|
|
if (!groupLayer.children || groupLayer.children.length === 0) {
|
|||
|
|
console.error(`组图层 ${this.groupId} 没有子图层`);
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 收集子图层ID
|
|||
|
|
this.childLayerIds = groupLayer.children.map((layer) => layer.id);
|
|||
|
|
|
|||
|
|
// 将子图层添加到原位置
|
|||
|
|
this.layers.value.splice(groupIndex, 1, ...groupLayer.children);
|
|||
|
|
|
|||
|
|
// 更新当前活动图层为第一个子图层
|
|||
|
|
if (this.childLayerIds.length > 0 && this.activeLayerId) {
|
|||
|
|
this.activeLayerId.value = this.childLayerIds[0];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return this.childLayerIds;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
undo() {
|
|||
|
|
// 恢复原始图层状态
|
|||
|
|
this.layers.value = [...this.originalLayers];
|
|||
|
|
// 恢复活动图层为原始组ID
|
|||
|
|
if (this.activeLayerId) {
|
|||
|
|
this.activeLayerId.value = this.groupId;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
getInfo() {
|
|||
|
|
return {
|
|||
|
|
name: this.name,
|
|||
|
|
groupId: this.groupId,
|
|||
|
|
childLayerIds: this.childLayerIds,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 合并图层内对象命令(重构版本)
|
|||
|
|
* 将新的图像与图层内现有对象合并为一个高保真图像对象
|
|||
|
|
*/
|
|||
|
|
export class MergeLayerObjectsCommand extends Command {
|
|||
|
|
constructor(options) {
|
|||
|
|
super({
|
|||
|
|
name: "合并图层内对象",
|
|||
|
|
saveState: true,
|
|||
|
|
});
|
|||
|
|
this.canvas = options.canvas;
|
|||
|
|
this.layers = options.layers;
|
|||
|
|
this.fabricImage = options.fabricImage;
|
|||
|
|
this.activeLayer = options.activeLayer;
|
|||
|
|
|
|||
|
|
// 备份原始对象,用于撤销
|
|||
|
|
if (this.activeLayer && Array.isArray(this.activeLayer.fabricObjects)) {
|
|||
|
|
this.originalObjects =
|
|||
|
|
this.canvas
|
|||
|
|
?.getObjects()
|
|||
|
|
?.filter((fItem) => fItem.layerId === this.activeLayer.id) || [];
|
|||
|
|
} else {
|
|||
|
|
this.originalObjects = [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 新合并图像对象
|
|||
|
|
this.mergedImage = null;
|
|||
|
|
this.newImageId = `merged_image_${Date.now()}_${Math.floor(
|
|||
|
|
Math.random() * 1000
|
|||
|
|
)}`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async execute() {
|
|||
|
|
if (!this.activeLayer || !this.canvas) {
|
|||
|
|
console.error("图层或Canvas未初始化");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取所有需要合并的对象(按正确的层级顺序)
|
|||
|
|
const objectsToMerge = [];
|
|||
|
|
|
|||
|
|
// 先添加图层中的现有对象(作为底层)
|
|||
|
|
if (this.originalObjects.length > 0) {
|
|||
|
|
objectsToMerge.push(...this.originalObjects);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 再添加新的图像对象(作为顶层)
|
|||
|
|
if (this.fabricImage) {
|
|||
|
|
objectsToMerge.push(this.fabricImage);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (objectsToMerge.length === 0) {
|
|||
|
|
console.log("没有对象需要合并");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算所有对象的合并边界
|
|||
|
|
const bounds = this._calculateMergedBounds(objectsToMerge);
|
|||
|
|
if (!bounds) {
|
|||
|
|
console.error("无法计算合并边界");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 异步处理图像合并
|
|||
|
|
try {
|
|||
|
|
const mergedImage = await this._createMergedImageAsync(
|
|||
|
|
objectsToMerge,
|
|||
|
|
bounds
|
|||
|
|
);
|
|||
|
|
this._setupMergedImage(mergedImage, bounds);
|
|||
|
|
this._replaceObjects(mergedImage);
|
|||
|
|
|
|||
|
|
console.log("图像合并完成");
|
|||
|
|
return this.newImageId;
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error("图像合并执行失败:", error);
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 异步创建合并图像
|
|||
|
|
* @param {Array} objectsToMerge 要合并的对象
|
|||
|
|
* @param {Object} bounds 边界信息
|
|||
|
|
* @returns {Promise<fabric.Image>} 合并后的图像
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
async _createMergedImageAsync(objectsToMerge, bounds) {
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
try {
|
|||
|
|
// 创建高保真临时画布
|
|||
|
|
const tempCanvas = this._createHighQualityTempCanvas(bounds);
|
|||
|
|
const tempFabricCanvas = createStaticCanvas(tempCanvas, {
|
|||
|
|
width: bounds.width,
|
|||
|
|
height: bounds.height,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 设置高质量渲染选项
|
|||
|
|
tempFabricCanvas.enableRetinaScaling = true;
|
|||
|
|
tempFabricCanvas.imageSmoothingEnabled = true;
|
|||
|
|
|
|||
|
|
// 将所有对象添加到临时画布并调整位置
|
|||
|
|
this._addObjectsToTempCanvas(tempFabricCanvas, objectsToMerge, bounds);
|
|||
|
|
|
|||
|
|
// 渲染临时画布
|
|||
|
|
tempFabricCanvas.renderAll();
|
|||
|
|
|
|||
|
|
// 生成高质量图像
|
|||
|
|
const dataUrl = tempFabricCanvas.toDataURL({
|
|||
|
|
format: "png",
|
|||
|
|
quality: 1.0,
|
|||
|
|
multiplier: 1,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 创建新的合并图像
|
|||
|
|
fabric.Image.fromURL(
|
|||
|
|
dataUrl,
|
|||
|
|
(mergedImg) => {
|
|||
|
|
try {
|
|||
|
|
// 清理临时资源
|
|||
|
|
this._cleanupTempCanvas(tempFabricCanvas);
|
|||
|
|
resolve(mergedImg);
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error("设置合并图像时发生错误:", error);
|
|||
|
|
this._cleanupTempCanvas(tempFabricCanvas);
|
|||
|
|
reject(error);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
crossOrigin: "anonymous",
|
|||
|
|
}
|
|||
|
|
);
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error("合并过程中发生错误:", error);
|
|||
|
|
reject(error);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 计算所有对象的合并边界
|
|||
|
|
* @param {Array} objects 要合并的对象数组
|
|||
|
|
* @returns {Object} 边界信息
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
_calculateMergedBounds(objects) {
|
|||
|
|
if (!objects || objects.length === 0) return null;
|
|||
|
|
|
|||
|
|
let minLeft = Infinity;
|
|||
|
|
let minTop = Infinity;
|
|||
|
|
let maxRight = -Infinity;
|
|||
|
|
let maxBottom = -Infinity;
|
|||
|
|
|
|||
|
|
objects.forEach((obj) => {
|
|||
|
|
if (!obj) return;
|
|||
|
|
|
|||
|
|
const bounds = obj.getBoundingRect(true, true);
|
|||
|
|
minLeft = Math.min(minLeft, bounds.left);
|
|||
|
|
minTop = Math.min(minTop, bounds.top);
|
|||
|
|
maxRight = Math.max(maxRight, bounds.left + bounds.width);
|
|||
|
|
maxBottom = Math.max(maxBottom, bounds.top + bounds.height);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const padding = 1;
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
left: minLeft - padding,
|
|||
|
|
top: minTop - padding,
|
|||
|
|
width: maxRight - minLeft + padding * 2,
|
|||
|
|
height: maxBottom - minTop + padding * 2,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 创建高质量临时画布
|
|||
|
|
* @param {Object} bounds 边界信息
|
|||
|
|
* @returns {HTMLCanvasElement} 临时画布
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
_createHighQualityTempCanvas(bounds) {
|
|||
|
|
const tempCanvas = document.createElement("canvas");
|
|||
|
|
tempCanvas.width = Math.ceil(bounds.width);
|
|||
|
|
tempCanvas.height = Math.ceil(bounds.height);
|
|||
|
|
tempCanvas.style.width = bounds.width + "px";
|
|||
|
|
tempCanvas.style.height = bounds.height + "px";
|
|||
|
|
|
|||
|
|
const ctx = tempCanvas.getContext("2d");
|
|||
|
|
ctx.imageSmoothingEnabled = true;
|
|||
|
|
ctx.imageSmoothingQuality = "high";
|
|||
|
|
|
|||
|
|
return tempCanvas;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 将对象添加到临时画布并调整位置
|
|||
|
|
* @param {fabric.Canvas} tempCanvas 临时画布
|
|||
|
|
* @param {Array} objects 对象数组
|
|||
|
|
* @param {Object} bounds 边界信息
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
_addObjectsToTempCanvas(tempCanvas, objects, bounds) {
|
|||
|
|
objects.forEach((obj, index) => {
|
|||
|
|
if (!obj) return;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const clonedObj = fabric.util.object.clone(obj);
|
|||
|
|
const objBounds = obj.getBoundingRect(true, true);
|
|||
|
|
const offsetX = objBounds.left - bounds.left;
|
|||
|
|
const offsetY = objBounds.top - bounds.top;
|
|||
|
|
|
|||
|
|
clonedObj.set({
|
|||
|
|
left: offsetX + (clonedObj.width * clonedObj.scaleX) / 2,
|
|||
|
|
top: offsetY + (clonedObj.height * clonedObj.scaleY) / 2,
|
|||
|
|
originX: "center",
|
|||
|
|
originY: "center",
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
tempCanvas.add(clonedObj);
|
|||
|
|
console.log(
|
|||
|
|
`添加对象 ${index + 1}/${objects.length}: ${obj.type || "unknown"}`
|
|||
|
|
);
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error(`添加对象到临时画布时发生错误:`, error);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 设置合并后的图像属性
|
|||
|
|
* @param {fabric.Image} mergedImg 合并后的图像
|
|||
|
|
* @param {Object} bounds 边界信息
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
_setupMergedImage(mergedImg, bounds) {
|
|||
|
|
mergedImg.set({
|
|||
|
|
id: this.newImageId,
|
|||
|
|
layerId: this.activeLayer.id,
|
|||
|
|
layerName: this.activeLayer.name,
|
|||
|
|
left: bounds.left + bounds.width / 2,
|
|||
|
|
top: bounds.top + bounds.height / 2,
|
|||
|
|
originX: "center",
|
|||
|
|
originY: "center",
|
|||
|
|
selectable: true,
|
|||
|
|
evented: true,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
this.mergedImage = mergedImg;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 替换原有对象为合并后的图像
|
|||
|
|
* @param {fabric.Image} mergedImg 合并后的图像
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
_replaceObjects(mergedImg) {
|
|||
|
|
if (!mergedImg || !this.canvas || !this.activeLayer) {
|
|||
|
|
console.error("_replaceObjects: 缺少必要的参数");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const wasRenderOnAddRemove = this.canvas.renderOnAddRemove;
|
|||
|
|
this.canvas.renderOnAddRemove = false;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 清空图层的对象列表
|
|||
|
|
this.activeLayer.fabricObjects = [];
|
|||
|
|
|
|||
|
|
// 添加合并后的图像到画布和图层
|
|||
|
|
this.canvas.add(mergedImg);
|
|||
|
|
this.activeLayer.fabricObjects.push(mergedImg);
|
|||
|
|
|
|||
|
|
// 从画布中移除所有原始对象
|
|||
|
|
this.originalObjects.forEach((obj) => {
|
|||
|
|
if (obj && this.canvas.getObjects().includes(obj)) {
|
|||
|
|
this.canvas.remove(obj);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 如果有新的图像对象,也要移除
|
|||
|
|
if (
|
|||
|
|
this.fabricImage &&
|
|||
|
|
this.canvas.getObjects().includes(this.fabricImage)
|
|||
|
|
) {
|
|||
|
|
this.canvas.remove(this.fabricImage);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log(
|
|||
|
|
`成功替换图层 ${this.activeLayer.name} 中的 ${this.originalObjects.length} 个对象为合并图像`
|
|||
|
|
);
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error("替换对象时发生错误:", error);
|
|||
|
|
} finally {
|
|||
|
|
this.canvas.renderOnAddRemove = wasRenderOnAddRemove;
|
|||
|
|
this.canvas.renderAll(); // 同步渲染画布
|
|||
|
|
|
|||
|
|
// 更新缩略图
|
|||
|
|
if (this.canvas.thumbnailManager) {
|
|||
|
|
setTimeout(() => {
|
|||
|
|
this.canvas.thumbnailManager.generateLayerThumbnail(
|
|||
|
|
this.activeLayer.id
|
|||
|
|
);
|
|||
|
|
}, 100);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
undo() {
|
|||
|
|
if (!this.activeLayer || !this.canvas) return;
|
|||
|
|
|
|||
|
|
const wasRenderOnAddRemove = this.canvas.renderOnAddRemove;
|
|||
|
|
this.canvas.renderOnAddRemove = false;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 移除合并后的图像
|
|||
|
|
if (this.mergedImage) {
|
|||
|
|
this.canvas.remove(this.mergedImage);
|
|||
|
|
|
|||
|
|
const imageIndex = this.activeLayer.fabricObjects.findIndex(
|
|||
|
|
(obj) => obj.id === this.newImageId
|
|||
|
|
);
|
|||
|
|
if (imageIndex !== -1) {
|
|||
|
|
this.activeLayer.fabricObjects.splice(imageIndex, 1);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 按原始顺序恢复对象到画布
|
|||
|
|
this.originalObjects.forEach((obj) => {
|
|||
|
|
if (obj) {
|
|||
|
|
this.canvas.add(obj);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 恢复图层对象列表
|
|||
|
|
this.activeLayer.fabricObjects = [...this.originalObjects];
|
|||
|
|
console.log(`成功撤销图层 ${this.activeLayer.name} 的对象合并操作`);
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error("撤销合并操作时发生错误:", error);
|
|||
|
|
} finally {
|
|||
|
|
this.canvas.renderOnAddRemove = wasRenderOnAddRemove;
|
|||
|
|
this.canvas.renderAll();
|
|||
|
|
|
|||
|
|
// 更新缩略图
|
|||
|
|
if (this.canvas.thumbnailManager) {
|
|||
|
|
setTimeout(() => {
|
|||
|
|
this.canvas.thumbnailManager.generateLayerThumbnail(
|
|||
|
|
this.activeLayer.id
|
|||
|
|
);
|
|||
|
|
}, 100);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 安全地清理临时画布资源
|
|||
|
|
* @param {fabric.Canvas} tempCanvas 临时画布
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
_cleanupTempCanvas(tempCanvas) {
|
|||
|
|
if (!tempCanvas) return;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
tempCanvas.clear();
|
|||
|
|
|
|||
|
|
const objects = tempCanvas.getObjects();
|
|||
|
|
objects.forEach((obj) => {
|
|||
|
|
tempCanvas.remove(obj);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const canvasEl = tempCanvas.getElement();
|
|||
|
|
if (canvasEl && canvasEl.getContext) {
|
|||
|
|
const ctx = canvasEl.getContext("2d");
|
|||
|
|
if (ctx) {
|
|||
|
|
ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (typeof tempCanvas.destroy === "function") {
|
|||
|
|
tempCanvas.destroy();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (canvasEl && canvasEl.parentNode) {
|
|||
|
|
canvasEl.parentNode.removeChild(canvasEl);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log("临时画布资源已安全清理");
|
|||
|
|
} catch (error) {
|
|||
|
|
console.warn("清理临时画布时发生警告:", error);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
getInfo() {
|
|||
|
|
return {
|
|||
|
|
name: this.name,
|
|||
|
|
layerId: this.activeLayer?.id,
|
|||
|
|
layerName: this.activeLayer?.name || "未知图层",
|
|||
|
|
originalObjectCount: this.originalObjects.length,
|
|||
|
|
hasNewImage: !!this.fabricImage,
|
|||
|
|
mergedImageId: this.newImageId,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 合并图层内对象成组的命令
|
|||
|
|
* 将新的图像与图层内现有对象合并为一个组对象
|
|||
|
|
* 支持向现有组添加对象,以及移除空组
|
|||
|
|
*/
|
|||
|
|
export class LayerObjectsToGroupCommand extends Command {
|
|||
|
|
constructor(options) {
|
|||
|
|
super({
|
|||
|
|
name: "图层内对象合并为组",
|
|||
|
|
saveState: true,
|
|||
|
|
});
|
|||
|
|
this.canvas = options.canvas;
|
|||
|
|
this.layers = options.layers;
|
|||
|
|
this.fabricImage = options.fabricImage;
|
|||
|
|
this.activeLayer = options.activeLayer;
|
|||
|
|
|
|||
|
|
// 备份原始对象,用于撤销
|
|||
|
|
if (this.activeLayer && Array.isArray(this.activeLayer.fabricObjects)) {
|
|||
|
|
this.originalObjects =
|
|||
|
|
this.canvas
|
|||
|
|
?.getObjects()
|
|||
|
|
?.filter((fItem) => fItem.layerId === this.activeLayer.id) || [];
|
|||
|
|
} else {
|
|||
|
|
this.originalObjects = [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 组对象相关
|
|||
|
|
this.existingGroup = null; // 现有的组对象
|
|||
|
|
this.groupObject = null; // 最终的组对象(可能是现有的或新创建的)
|
|||
|
|
this.newGroupId = `group_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
|
|||
|
|
this.wasGroupCreated = false; // 是否创建了新组
|
|||
|
|
this.addedObjectsToGroup = []; // 添加到组中的新对象
|
|||
|
|
this.originalLayerObjects = [...(this.activeLayer.fabricObjects || [])]; // 备份图层原始对象列表
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async execute() {
|
|||
|
|
if (!this.activeLayer || !this.canvas) {
|
|||
|
|
console.error("图层或Canvas未初始化");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 查找图层中是否已有组对象
|
|||
|
|
this.existingGroup = this._findExistingGroup();
|
|||
|
|
|
|||
|
|
// 准备要添加的新对象
|
|||
|
|
const newObjectsToAdd = [];
|
|||
|
|
if (this.fabricImage) {
|
|||
|
|
newObjectsToAdd.push(this.fabricImage);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (newObjectsToAdd.length === 0) {
|
|||
|
|
console.log("没有新对象需要添加到组");
|
|||
|
|
return this.existingGroup?.id || null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 暂时禁用画布自动渲染
|
|||
|
|
const wasRenderOnAddRemove = this.canvas.renderOnAddRemove;
|
|||
|
|
this.canvas.renderOnAddRemove = false;
|
|||
|
|
|
|||
|
|
if (this.existingGroup) {
|
|||
|
|
// 如果已有组,将新对象添加到现有组中
|
|||
|
|
this.groupObject = this.existingGroup;
|
|||
|
|
this._addObjectsToExistingGroup(newObjectsToAdd);
|
|||
|
|
console.log(`✅ 将 ${newObjectsToAdd.length} 个对象添加到现有组中`);
|
|||
|
|
} else {
|
|||
|
|
// 如果没有组,创建新组
|
|||
|
|
this._createNewGroupWithAllObjects(newObjectsToAdd);
|
|||
|
|
this.wasGroupCreated = true;
|
|||
|
|
console.log(
|
|||
|
|
`✅ 创建新组并添加 ${
|
|||
|
|
this.originalObjects.length + newObjectsToAdd.length
|
|||
|
|
} 个对象`
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 恢复画布自动渲染并重新渲染
|
|||
|
|
this.canvas.renderOnAddRemove = wasRenderOnAddRemove;
|
|||
|
|
this.canvas.renderAll();
|
|||
|
|
|
|||
|
|
// 选中组对象
|
|||
|
|
// this.canvas.setActiveObject(this.groupObject);
|
|||
|
|
|
|||
|
|
// 更新缩略图
|
|||
|
|
this._updateThumbnail();
|
|||
|
|
|
|||
|
|
return this.groupObject.id;
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error("执行组合操作时发生错误:", error);
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 查找图层中现有的组对象
|
|||
|
|
* @returns {fabric.Group|null}
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
_findExistingGroup() {
|
|||
|
|
if (!this.activeLayer.fabricObjects) return null;
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
this.activeLayer.fabricObjects.find(
|
|||
|
|
(obj) => obj && obj.type === "group"
|
|||
|
|
) || null
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 将新对象添加到现有组中
|
|||
|
|
* @param {Array} newObjects 要添加的新对象
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
_addObjectsToExistingGroup(newObjects) {
|
|||
|
|
// 先从画布移除新对象(避免重复添加)
|
|||
|
|
newObjects.forEach((obj) => {
|
|||
|
|
if (this.canvas.getObjects().includes(obj)) {
|
|||
|
|
this.canvas.remove(obj);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 使用 addWithUpdate 方法正确添加对象到组
|
|||
|
|
newObjects.forEach((obj) => {
|
|||
|
|
this.existingGroup.addWithUpdate(obj);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 记录添加的对象,用于撤销
|
|||
|
|
this.addedObjectsToGroup = newObjects;
|
|||
|
|
|
|||
|
|
// 组对象引用不变,只是内容更新了
|
|||
|
|
this.groupObject = this.existingGroup;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 创建包含所有对象的新组
|
|||
|
|
* @param {Array} newObjects 新添加的对象
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
_createNewGroupWithAllObjects(newObjects) {
|
|||
|
|
const allObjects = [...this.originalObjects, ...newObjects];
|
|||
|
|
|
|||
|
|
// 从画布中移除所有要组合的对象
|
|||
|
|
allObjects.forEach((obj) => {
|
|||
|
|
if (obj && this.canvas.getObjects().includes(obj)) {
|
|||
|
|
this.canvas.remove(obj);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 创建组对象 - Fabric.js 5 中创建组时会自动将对象从画布移除
|
|||
|
|
// 不需要手动从画布中移除对象
|
|||
|
|
this.groupObject = new fabric.Group(allObjects, {
|
|||
|
|
id: this.newGroupId,
|
|||
|
|
layerId: this.activeLayer.id,
|
|||
|
|
layerName: this.activeLayer.name,
|
|||
|
|
selectable: true,
|
|||
|
|
evented: true,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 添加组对象到画布
|
|||
|
|
this.canvas.add(this.groupObject);
|
|||
|
|
|
|||
|
|
// 更新图层的对象列表 - 只包含组对象
|
|||
|
|
this.activeLayer.fabricObjects = [this.groupObject];
|
|||
|
|
|
|||
|
|
// 记录添加的对象
|
|||
|
|
this.addedObjectsToGroup = newObjects;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
undo() {
|
|||
|
|
if (!this.activeLayer || !this.canvas || !this.groupObject) return;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 暂时禁用画布自动渲染
|
|||
|
|
const wasRenderOnAddRemove = this.canvas.renderOnAddRemove;
|
|||
|
|
this.canvas.renderOnAddRemove = false;
|
|||
|
|
|
|||
|
|
if (this.wasGroupCreated) {
|
|||
|
|
// 如果是新创建的组,完全撤销到原始状态
|
|||
|
|
this._undoNewGroupCreation();
|
|||
|
|
} else {
|
|||
|
|
// 如果是向现有组添加对象,只移除新添加的对象
|
|||
|
|
this._undoAddToExistingGroup();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 恢复画布自动渲染并重新渲染
|
|||
|
|
this.canvas.renderOnAddRemove = wasRenderOnAddRemove;
|
|||
|
|
this.canvas.renderAll();
|
|||
|
|
|
|||
|
|
// 更新缩略图
|
|||
|
|
this._updateThumbnail();
|
|||
|
|
|
|||
|
|
console.log(`↩️ 成功撤销图层 ${this.activeLayer.name} 的对象组合操作`);
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error("撤销组合操作时发生错误:", error);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 撤销新组的创建
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
_undoNewGroupCreation() {
|
|||
|
|
// 从画布中移除组对象
|
|||
|
|
if (this.canvas.getObjects().includes(this.groupObject)) {
|
|||
|
|
this.canvas.remove(this.groupObject);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 解散组 - 使用 destroy 方法或直接处理
|
|||
|
|
if (typeof this.groupObject.destroy === "function") {
|
|||
|
|
this.groupObject?.destroy?.();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 将原始对象重新添加到画布
|
|||
|
|
// 注意:解散组后,对象会自动恢复到画布上,但需要重新设置属性
|
|||
|
|
this.originalObjects.forEach((obj) => {
|
|||
|
|
if (obj && !this.canvas.getObjects().includes(obj)) {
|
|||
|
|
// 恢复对象的原始属性
|
|||
|
|
obj.layerId = this.activeLayer.id;
|
|||
|
|
obj.layerName = this.activeLayer.name;
|
|||
|
|
this.canvas.add(obj);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 恢复图层对象列表
|
|||
|
|
this.activeLayer.fabricObjects = [...this.originalObjects];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 撤销向现有组添加对象的操作
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
_undoAddToExistingGroup() {
|
|||
|
|
// 使用 removeWithUpdate 方法从组中移除新添加的对象
|
|||
|
|
// 在 Fabric.js 5 中,removeWithUpdate 会自动将对象重新添加到画布
|
|||
|
|
this.addedObjectsToGroup.forEach((obj) => {
|
|||
|
|
this.existingGroup.removeWithUpdate(obj);
|
|||
|
|
|
|||
|
|
// 重新设置对象的图层关联
|
|||
|
|
obj.layerId = this.activeLayer.id;
|
|||
|
|
obj.layerName = this.activeLayer.name;
|
|||
|
|
|
|||
|
|
// 确保对象在图层对象列表中
|
|||
|
|
if (!this.activeLayer.fabricObjects.includes(obj)) {
|
|||
|
|
this.activeLayer.fabricObjects.push(obj);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 检查组是否变为空或只有一个对象
|
|||
|
|
const remainingObjects = this.existingGroup.getObjects();
|
|||
|
|
if (remainingObjects.length === 0) {
|
|||
|
|
// 组为空,移除组
|
|||
|
|
this.canvas.remove(this.existingGroup);
|
|||
|
|
const groupIndex = this.activeLayer.fabricObjects.indexOf(
|
|||
|
|
this.existingGroup
|
|||
|
|
);
|
|||
|
|
if (groupIndex !== -1) {
|
|||
|
|
this.activeLayer.fabricObjects.splice(groupIndex, 1);
|
|||
|
|
}
|
|||
|
|
} else if (remainingObjects.length === 1) {
|
|||
|
|
// 只剩一个对象,解散组
|
|||
|
|
const singleObj = remainingObjects[0];
|
|||
|
|
|
|||
|
|
// 从画布移除组
|
|||
|
|
this.canvas.remove(this.existingGroup);
|
|||
|
|
|
|||
|
|
// 使用 removeWithUpdate 移除最后一个对象
|
|||
|
|
// 这会自动将对象添加回画布
|
|||
|
|
this.existingGroup.removeWithUpdate(singleObj);
|
|||
|
|
|
|||
|
|
// 重新设置对象属性
|
|||
|
|
singleObj.layerId = this.activeLayer.id;
|
|||
|
|
singleObj.layerName = this.activeLayer.name;
|
|||
|
|
|
|||
|
|
// 更新图层对象列表
|
|||
|
|
const groupIndex = this.activeLayer.fabricObjects.indexOf(
|
|||
|
|
this.existingGroup
|
|||
|
|
);
|
|||
|
|
if (groupIndex !== -1) {
|
|||
|
|
this.activeLayer.fabricObjects[groupIndex] = singleObj;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 更新缩略图
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
_updateThumbnail() {
|
|||
|
|
if (this.canvas.thumbnailManager) {
|
|||
|
|
setTimeout(() => {
|
|||
|
|
this.canvas.thumbnailManager.generateLayerThumbnail(
|
|||
|
|
this.activeLayer.id
|
|||
|
|
);
|
|||
|
|
}, 100);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
getInfo() {
|
|||
|
|
return {
|
|||
|
|
name: this.name,
|
|||
|
|
layerId: this.activeLayer?.id,
|
|||
|
|
layerName: this.activeLayer?.name || "未知图层",
|
|||
|
|
originalObjectCount: this.originalObjects.length,
|
|||
|
|
hasNewImage: !!this.fabricImage,
|
|||
|
|
groupId: this.groupObject?.id,
|
|||
|
|
wasGroupCreated: this.wasGroupCreated,
|
|||
|
|
addedObjectsCount: this.addedObjectsToGroup.length,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 创建图片图层复合命令
|
|||
|
|
* 包含创建图层、添加对象到图层、切换工具等操作的复合命令
|
|||
|
|
*/
|
|||
|
|
export class CreateImageLayerCommand extends Command {
|
|||
|
|
constructor(options) {
|
|||
|
|
super({
|
|||
|
|
name: "创建图片图层",
|
|||
|
|
saveState: true,
|
|||
|
|
});
|
|||
|
|
this.layerManager = options.layerManager;
|
|||
|
|
this.fabricImage = options.fabricImage;
|
|||
|
|
this.toolManager = options.toolManager;
|
|||
|
|
this.layerName = options.layerName || null;
|
|||
|
|
|
|||
|
|
// 存储执行过程中的结果
|
|||
|
|
this.newLayerId = null;
|
|||
|
|
this.commands = [];
|
|||
|
|
this.executedCommands = [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async execute() {
|
|||
|
|
if (!this.layerManager || !this.fabricImage) {
|
|||
|
|
throw new Error("图层管理器或图片对象无效");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
this.commands = [];
|
|||
|
|
this.executedCommands = [];
|
|||
|
|
|
|||
|
|
// 生成图层名称
|
|||
|
|
const fileName =
|
|||
|
|
this.layerName || `图片 ${new Date().toLocaleTimeString()}`;
|
|||
|
|
|
|||
|
|
// 1. 创建新图层命令
|
|||
|
|
const createLayerCmd = new AddLayerCommand({
|
|||
|
|
canvas: this.layerManager.canvas,
|
|||
|
|
layers: this.layerManager.layers,
|
|||
|
|
newLayer: createLayer({
|
|||
|
|
name: fileName,
|
|||
|
|
type: LayerType.BITMAP,
|
|||
|
|
visible: true,
|
|||
|
|
locked: false,
|
|||
|
|
opacity: 1.0,
|
|||
|
|
fabricObjects: [],
|
|||
|
|
}),
|
|||
|
|
activeLayerId: this.layerManager.activeLayerId,
|
|||
|
|
insertIndex: this.layerManager._getInsertIndexAboveActiveLayer(),
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 执行创建图层命令
|
|||
|
|
this.newLayerId = await createLayerCmd.execute();
|
|||
|
|
this.commands.push(createLayerCmd);
|
|||
|
|
this.executedCommands.push(createLayerCmd);
|
|||
|
|
|
|||
|
|
// 2. 添加图片对象到图层命令
|
|||
|
|
const addObjectCmd = new AddObjectToLayerCommand({
|
|||
|
|
canvas: this.layerManager.canvas,
|
|||
|
|
layers: this.layerManager.layers,
|
|||
|
|
layerId: this.newLayerId,
|
|||
|
|
fabricObject: this.fabricImage,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 执行添加对象命令
|
|||
|
|
await addObjectCmd.execute();
|
|||
|
|
this.commands.push(addObjectCmd);
|
|||
|
|
this.executedCommands.push(addObjectCmd);
|
|||
|
|
|
|||
|
|
// 3. 切换工具到选择模式命令
|
|||
|
|
if (this.toolManager) {
|
|||
|
|
const toolCmd = new ToolCommand({
|
|||
|
|
toolManager: this.toolManager,
|
|||
|
|
tool: OperationType.SELECT,
|
|||
|
|
previousTool: this.toolManager.getCurrentTool(),
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 执行工具切换命令
|
|||
|
|
await toolCmd.execute();
|
|||
|
|
this.commands.push(toolCmd);
|
|||
|
|
this.executedCommands.push(toolCmd);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log(`✅ 创建图片图层完成: ${fileName}, ID: ${this.newLayerId}`);
|
|||
|
|
return this.newLayerId;
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error("创建图片图层失败:", error);
|
|||
|
|
// 回滚已执行的命令
|
|||
|
|
await this._rollbackExecutedCommands();
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async undo() {
|
|||
|
|
if (this.executedCommands.length === 0) {
|
|||
|
|
console.warn("没有已执行的命令需要撤销");
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log(
|
|||
|
|
`↩️ 开始撤销创建图片图层操作,共 ${this.executedCommands.length} 个子命令`
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 逆序撤销已执行的命令
|
|||
|
|
const commands = [...this.executedCommands].reverse();
|
|||
|
|
|
|||
|
|
for (const command of commands) {
|
|||
|
|
if (typeof command.undo === "function") {
|
|||
|
|
try {
|
|||
|
|
console.log(`↩️ 撤销子命令: ${command.constructor.name}`);
|
|||
|
|
const result = command.undo();
|
|||
|
|
if (this._isPromise(result)) {
|
|||
|
|
await result;
|
|||
|
|
}
|
|||
|
|
console.log(`✅ 子命令撤销成功: ${command.constructor.name}`);
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error(
|
|||
|
|
`❌ 子命令撤销失败: ${command.constructor.name}`,
|
|||
|
|
error
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.executedCommands = [];
|
|||
|
|
console.log(`✅ 创建图片图层撤销完成`);
|
|||
|
|
return true;
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error("❌ 撤销创建图片图层过程中发生错误:", error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 回滚已执行的命令(内部使用)
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
async _rollbackExecutedCommands() {
|
|||
|
|
console.log(`🔄 开始回滚已执行的 ${this.executedCommands.length} 个子命令`);
|
|||
|
|
|
|||
|
|
const commands = [...this.executedCommands].reverse();
|
|||
|
|
|
|||
|
|
for (const command of commands) {
|
|||
|
|
if (typeof command.undo === "function") {
|
|||
|
|
try {
|
|||
|
|
console.log(`🔄 回滚子命令: ${command.constructor.name}`);
|
|||
|
|
const result = command.undo();
|
|||
|
|
if (this._isPromise(result)) {
|
|||
|
|
await result;
|
|||
|
|
}
|
|||
|
|
console.log(`✅ 子命令回滚成功: ${command.constructor.name}`);
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error(
|
|||
|
|
`❌ 子命令回滚失败: ${command.constructor.name}`,
|
|||
|
|
error
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.executedCommands = [];
|
|||
|
|
console.log(`✅ 回滚完成`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 检查返回值是否为Promise
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
_isPromise(value) {
|
|||
|
|
return (
|
|||
|
|
value &&
|
|||
|
|
typeof value === "object" &&
|
|||
|
|
typeof value.then === "function" &&
|
|||
|
|
typeof value.catch === "function"
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
getInfo() {
|
|||
|
|
return {
|
|||
|
|
name: this.name,
|
|||
|
|
layerName: this.layerName,
|
|||
|
|
layerId: this.newLayerId,
|
|||
|
|
commandCount: this.commands.length,
|
|||
|
|
executedCount: this.executedCommands.length,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 图层重新排序命令
|
|||
|
|
*/
|
|||
|
|
export class ReorderLayersCommand extends Command {
|
|||
|
|
constructor(options) {
|
|||
|
|
super({
|
|||
|
|
name: "图层重新排序",
|
|||
|
|
saveState: false,
|
|||
|
|
});
|
|||
|
|
this.layers = options.layers;
|
|||
|
|
this.oldIndex = options.oldIndex;
|
|||
|
|
this.newIndex = options.newIndex;
|
|||
|
|
this.layerId = options.layerId;
|
|||
|
|
|
|||
|
|
// 验证参数
|
|||
|
|
if (this.oldIndex === undefined || this.newIndex === undefined) {
|
|||
|
|
throw new Error("缺少必要的索引参数");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (this.oldIndex === this.newIndex) {
|
|||
|
|
console.warn("新旧索引相同,无需排序");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
execute() {
|
|||
|
|
if (
|
|||
|
|
!this.layers ||
|
|||
|
|
!this.layers.value ||
|
|||
|
|
!Array.isArray(this.layers.value)
|
|||
|
|
) {
|
|||
|
|
console.error("图层数组无效");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (this.oldIndex < 0 || this.oldIndex >= this.layers.value.length) {
|
|||
|
|
console.error("原始索引超出范围");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (this.newIndex < 0 || this.newIndex >= this.layers.value.length) {
|
|||
|
|
console.error("目标索引超出范围");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取要移动的图层
|
|||
|
|
const layerToMove = this.layers.value[this.oldIndex];
|
|||
|
|
|
|||
|
|
if (!layerToMove) {
|
|||
|
|
console.error("找不到要移动的图层");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证图层ID是否匹配
|
|||
|
|
if (this.layerId && layerToMove.id !== this.layerId) {
|
|||
|
|
console.error("图层ID不匹配");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 执行重新排序
|
|||
|
|
this.layers.value.splice(this.oldIndex, 1);
|
|||
|
|
this.layers.value.splice(this.newIndex, 0, layerToMove);
|
|||
|
|
|
|||
|
|
console.log(
|
|||
|
|
`✅ 图层 "${layerToMove.name}" 从索引 ${this.oldIndex} 移动到 ${this.newIndex}`
|
|||
|
|
);
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
undo() {
|
|||
|
|
if (
|
|||
|
|
!this.layers ||
|
|||
|
|
!this.layers.value ||
|
|||
|
|
!Array.isArray(this.layers.value)
|
|||
|
|
) {
|
|||
|
|
console.error("图层数组无效");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (this.newIndex < 0 || this.newIndex >= this.layers.value.length) {
|
|||
|
|
console.error("当前索引超出范围");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取当前在新位置的图层
|
|||
|
|
const layerToRestore = this.layers.value[this.newIndex];
|
|||
|
|
|
|||
|
|
if (!layerToRestore) {
|
|||
|
|
console.error("找不到要恢复的图层");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证图层ID是否匹配
|
|||
|
|
if (this.layerId && layerToRestore.id !== this.layerId) {
|
|||
|
|
console.error("图层ID不匹配");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 恢复原始排序
|
|||
|
|
this.layers.value.splice(this.newIndex, 1);
|
|||
|
|
this.layers.value.splice(this.oldIndex, 0, layerToRestore);
|
|||
|
|
|
|||
|
|
console.log(
|
|||
|
|
`↩️ 图层 "${layerToRestore.name}" 恢复到原始位置 ${this.oldIndex}`
|
|||
|
|
);
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
getInfo() {
|
|||
|
|
return {
|
|||
|
|
name: this.name,
|
|||
|
|
layerId: this.layerId,
|
|||
|
|
oldIndex: this.oldIndex,
|
|||
|
|
newIndex: this.newIndex,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 子图层重新排序命令
|
|||
|
|
*/
|
|||
|
|
export class ReorderChildLayersCommand extends Command {
|
|||
|
|
constructor(options) {
|
|||
|
|
super({
|
|||
|
|
name: "子图层重新排序",
|
|||
|
|
saveState: false,
|
|||
|
|
});
|
|||
|
|
this.layers = options.layers;
|
|||
|
|
this.parentId = options.parentId;
|
|||
|
|
this.oldIndex = options.oldIndex;
|
|||
|
|
this.newIndex = options.newIndex;
|
|||
|
|
this.layerId = options.layerId;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
execute() {
|
|||
|
|
// 查找父图层
|
|||
|
|
const parentLayer = this.layers.value.find(
|
|||
|
|
(layer) => layer.id === this.parentId
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
if (!parentLayer || !parentLayer.children) {
|
|||
|
|
console.error("找不到父图层或父图层没有子图层");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 执行重新排序
|
|||
|
|
const childToMove = parentLayer.children[this.oldIndex];
|
|||
|
|
parentLayer.children.splice(this.oldIndex, 1);
|
|||
|
|
parentLayer.children.splice(this.newIndex, 0, childToMove);
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
undo() {
|
|||
|
|
// 查找父图层
|
|||
|
|
const parentLayer = this.layers.value.find(
|
|||
|
|
(layer) => layer.id === this.parentId
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
if (!parentLayer || !parentLayer.children) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 恢复原始排序
|
|||
|
|
const childToRestore = parentLayer.children[this.newIndex];
|
|||
|
|
parentLayer.children.splice(this.newIndex, 1);
|
|||
|
|
parentLayer.children.splice(this.oldIndex, 0, childToRestore);
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
getInfo() {
|
|||
|
|
return {
|
|||
|
|
name: this.name,
|
|||
|
|
parentId: this.parentId,
|
|||
|
|
layerId: this.layerId,
|
|||
|
|
oldIndex: this.oldIndex,
|
|||
|
|
newIndex: this.newIndex,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 更改固定图层图像命令
|
|||
|
|
* 用于更换固定图层(红绿图模式、模特图层等)的图像内容
|
|||
|
|
*/
|
|||
|
|
export class ChangeFixedImageCommand extends Command {
|
|||
|
|
constructor(options) {
|
|||
|
|
super({
|
|||
|
|
name: "更改固定图层图像",
|
|||
|
|
saveState: true,
|
|||
|
|
});
|
|||
|
|
this.canvas = options.canvas;
|
|||
|
|
this.layers = options.layers;
|
|||
|
|
this.layerId = options.layerId;
|
|||
|
|
this.newImageFile = options.newImageFile;
|
|||
|
|
this.layerManager = options.layerManager;
|
|||
|
|
|
|||
|
|
// 备份原始数据
|
|||
|
|
this.originalLayer = null;
|
|||
|
|
this.originalObject = null;
|
|||
|
|
this.newImageObject = null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async execute() {
|
|||
|
|
if (!this.canvas || !this.layers || !this.layerId || !this.newImageFile) {
|
|||
|
|
throw new Error("更改固定图层图像命令缺少必要参数");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 查找目标固定图层
|
|||
|
|
this.originalLayer = this.layers.value.find(
|
|||
|
|
(layer) => layer.id === this.layerId
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
if (!this.originalLayer) {
|
|||
|
|
throw new Error(`找不到图层 ID: ${this.layerId}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证是否为固定图层
|
|||
|
|
if (!this.originalLayer.isFixed && !this.originalLayer.isBackground) {
|
|||
|
|
throw new Error("只能更改固定图层或背景图层的图像");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 备份原始对象
|
|||
|
|
if (this.originalLayer.fabricObject) {
|
|||
|
|
this.originalObject = this.originalLayer.fabricObject;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建新的图像对象
|
|||
|
|
this.newImageObject = await this._createImageFromFile(this.newImageFile);
|
|||
|
|
|
|||
|
|
// 设置新图像的属性
|
|||
|
|
this._setupNewImageProperties();
|
|||
|
|
|
|||
|
|
// 替换图层中的对象
|
|||
|
|
this._replaceLayerObject();
|
|||
|
|
|
|||
|
|
// 重新渲染画布
|
|||
|
|
this.canvas.renderAll();
|
|||
|
|
|
|||
|
|
console.log(`✅ 成功更改固定图层 "${this.originalLayer.name}" 的图像`);
|
|||
|
|
return this.newImageObject.id;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 从文件创建图像对象
|
|||
|
|
* @param {File} file 图像文件
|
|||
|
|
* @returns {Promise<fabric.Image>} 图像对象
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
async _createImageFromFile(file) {
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
const reader = new FileReader();
|
|||
|
|
reader.onload = (e) => {
|
|||
|
|
fabric.Image.fromURL(
|
|||
|
|
e.target.result,
|
|||
|
|
(img) => {
|
|||
|
|
if (!img) {
|
|||
|
|
reject(new Error("无法创建图像对象"));
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
resolve(img);
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
crossOrigin: "anonymous",
|
|||
|
|
}
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
reader.onerror = () => reject(new Error("文件读取失败"));
|
|||
|
|
reader.readAsDataURL(file);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 设置新图像的属性
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
_setupNewImageProperties() {
|
|||
|
|
if (!this.newImageObject || !this.originalLayer) return;
|
|||
|
|
|
|||
|
|
// 生成新的ID
|
|||
|
|
const newId = `fixed_image_${Date.now()}_${Math.floor(
|
|||
|
|
Math.random() * 1000
|
|||
|
|
)}`;
|
|||
|
|
|
|||
|
|
// 基本属性设置
|
|||
|
|
this.newImageObject.set({
|
|||
|
|
id: newId,
|
|||
|
|
layerId: this.originalLayer.id,
|
|||
|
|
layerName: this.originalLayer.name,
|
|||
|
|
selectable: false, // 固定图层通常不可选择
|
|||
|
|
evented: false, // 固定图层通常不响应事件
|
|||
|
|
visible: this.originalLayer.visible,
|
|||
|
|
opacity: this.originalLayer.opacity || 1.0,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 如果有原始对象,继承其位置和变换属性
|
|||
|
|
if (this.originalObject) {
|
|||
|
|
this.newImageObject.set({
|
|||
|
|
left: this.originalObject.left,
|
|||
|
|
top: this.originalObject.top,
|
|||
|
|
scaleX: this.originalObject.scaleX,
|
|||
|
|
scaleY: this.originalObject.scaleY,
|
|||
|
|
angle: this.originalObject.angle,
|
|||
|
|
flipX: this.originalObject.flipX,
|
|||
|
|
flipY: this.originalObject.flipY,
|
|||
|
|
originX: this.originalObject.originX || "center",
|
|||
|
|
originY: this.originalObject.originY || "center",
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
// 如果没有原始对象,设置默认居中位置
|
|||
|
|
const canvasCenter = this.canvas.getCenter();
|
|||
|
|
this.newImageObject.set({
|
|||
|
|
left: canvasCenter.left,
|
|||
|
|
top: canvasCenter.top,
|
|||
|
|
originX: "center",
|
|||
|
|
originY: "center",
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果是背景图层,可能需要特殊处理
|
|||
|
|
if (this.originalLayer.isBackground) {
|
|||
|
|
this.newImageObject.set({
|
|||
|
|
isBackground: true,
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 替换图层中的对象
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
_replaceLayerObject() {
|
|||
|
|
// 从画布移除原始对象
|
|||
|
|
if (
|
|||
|
|
this.originalObject &&
|
|||
|
|
this.canvas.getObjects().includes(this.originalObject)
|
|||
|
|
) {
|
|||
|
|
this.canvas.remove(this.originalObject);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加新对象到画布
|
|||
|
|
this.canvas.add(this.newImageObject);
|
|||
|
|
|
|||
|
|
// 更新图层引用
|
|||
|
|
this.originalLayer.fabricObject = this.newImageObject;
|
|||
|
|
|
|||
|
|
// 如果图层有 fabricObjects 数组,也要更新
|
|||
|
|
if (this.originalLayer.fabricObjects) {
|
|||
|
|
if (this.originalObject) {
|
|||
|
|
const index = this.originalLayer.fabricObjects.indexOf(
|
|||
|
|
this.originalObject
|
|||
|
|
);
|
|||
|
|
if (index !== -1) {
|
|||
|
|
this.originalLayer.fabricObjects[index] = this.newImageObject;
|
|||
|
|
} else {
|
|||
|
|
this.originalLayer.fabricObjects.push(this.newImageObject);
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
this.originalLayer.fabricObjects.push(this.newImageObject);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 确保图层顺序正确(固定图层通常在特定位置)
|
|||
|
|
this._adjustObjectZIndex();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 调整对象的Z轴顺序
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
_adjustObjectZIndex() {
|
|||
|
|
if (!this.newImageObject || !this.originalLayer) return;
|
|||
|
|
|
|||
|
|
// 如果是背景图层,确保在最底层
|
|||
|
|
if (this.originalLayer.isBackground) {
|
|||
|
|
this.newImageObject.sendToBack();
|
|||
|
|
} else if (this.originalLayer.isFixed) {
|
|||
|
|
// 如果是固定图层,放在背景层之上,普通图层之下
|
|||
|
|
const backgroundObjects = this.canvas
|
|||
|
|
.getObjects()
|
|||
|
|
.filter((obj) => obj.isBackground);
|
|||
|
|
if (backgroundObjects.length > 0) {
|
|||
|
|
this.newImageObject.bringForward(); // 向前移动一层
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async undo() {
|
|||
|
|
if (!this.canvas || !this.originalLayer) {
|
|||
|
|
console.error("无法撤销:缺少必要的状态信息");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 从画布移除新对象
|
|||
|
|
if (
|
|||
|
|
this.newImageObject &&
|
|||
|
|
this.canvas.getObjects().includes(this.newImageObject)
|
|||
|
|
) {
|
|||
|
|
this.canvas.remove(this.newImageObject);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 恢复原始对象
|
|||
|
|
if (this.originalObject) {
|
|||
|
|
this.canvas.add(this.originalObject);
|
|||
|
|
this.originalLayer.fabricObject = this.originalObject;
|
|||
|
|
|
|||
|
|
// 恢复 fabricObjects 数组
|
|||
|
|
if (this.originalLayer.fabricObjects) {
|
|||
|
|
const newIndex = this.originalLayer.fabricObjects.indexOf(
|
|||
|
|
this.newImageObject
|
|||
|
|
);
|
|||
|
|
if (newIndex !== -1) {
|
|||
|
|
this.originalLayer.fabricObjects[newIndex] = this.originalObject;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 如果原来没有对象,清空图层引用
|
|||
|
|
this.originalLayer.fabricObject = null;
|
|||
|
|
if (this.originalLayer.fabricObjects) {
|
|||
|
|
this.originalLayer.fabricObjects =
|
|||
|
|
this.originalLayer.fabricObjects.filter(
|
|||
|
|
(obj) => obj !== this.newImageObject
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 重新渲染画布
|
|||
|
|
this.canvas.renderAll();
|
|||
|
|
|
|||
|
|
console.log(
|
|||
|
|
`↩️ 成功撤销固定图层 "${this.originalLayer.name}" 的图像更改`
|
|||
|
|
);
|
|||
|
|
return true;
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error("撤销更改固定图层图像时发生错误:", error);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
getInfo() {
|
|||
|
|
return {
|
|||
|
|
name: this.name,
|
|||
|
|
layerId: this.layerId,
|
|||
|
|
layerName: this.originalLayer?.name || "未知图层",
|
|||
|
|
fileName: this.newImageFile?.name || "未知文件",
|
|||
|
|
hasOriginalObject: !!this.originalObject,
|
|||
|
|
newImageId: this.newImageObject?.id,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 添加图像到图层命令
|
|||
|
|
* 如果指定图层ID则添加到指定图层,否则创建新图层
|
|||
|
|
*/
|
|||
|
|
export class AddImageToLayerCommand extends Command {
|
|||
|
|
constructor(options) {
|
|||
|
|
super({
|
|||
|
|
name: "添加图像到图层",
|
|||
|
|
saveState: true,
|
|||
|
|
});
|
|||
|
|
this.canvas = options.canvas;
|
|||
|
|
this.layers = options.layers;
|
|||
|
|
this.activeLayerId = options.activeLayerId;
|
|||
|
|
this.imageFile = options.imageFile;
|
|||
|
|
this.targetLayerId = options.targetLayerId; // 可选:指定目标图层ID
|
|||
|
|
this.layerManager = options.layerManager;
|
|||
|
|
this.toolManager = options.toolManager;
|
|||
|
|
|
|||
|
|
// 执行结果
|
|||
|
|
this.createdImageObject = null;
|
|||
|
|
this.usedLayerId = null;
|
|||
|
|
this.wasLayerCreated = false;
|
|||
|
|
this.subCommands = [];
|
|||
|
|
this.executedSubCommands = [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async execute() {
|
|||
|
|
if (!this.canvas || !this.layers || !this.imageFile) {
|
|||
|
|
throw new Error("添加图像到图层命令缺少必要参数");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 创建图像对象
|
|||
|
|
this.createdImageObject = await this._createImageFromFile(this.imageFile);
|
|||
|
|
|
|||
|
|
// 确定目标图层
|
|||
|
|
const targetLayer = await this._determineTargetLayer();
|
|||
|
|
this.usedLayerId = targetLayer.id;
|
|||
|
|
|
|||
|
|
// 添加图像到图层
|
|||
|
|
await this._addImageToLayer(targetLayer);
|
|||
|
|
|
|||
|
|
// 切换到选择工具
|
|||
|
|
await this._switchToSelectTool();
|
|||
|
|
|
|||
|
|
console.log(`✅ 成功添加图像到图层 "${targetLayer.name}"`);
|
|||
|
|
return {
|
|||
|
|
layerId: this.usedLayerId,
|
|||
|
|
imageId: this.createdImageObject.id,
|
|||
|
|
wasLayerCreated: this.wasLayerCreated,
|
|||
|
|
};
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error("添加图像到图层失败:", error);
|
|||
|
|
// 回滚已执行的子命令
|
|||
|
|
await this._rollbackSubCommands();
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 从文件创建图像对象
|
|||
|
|
* @param {File} file 图像文件
|
|||
|
|
* @returns {Promise<fabric.Image>} 图像对象
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
async _createImageFromFile(file) {
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
const reader = new FileReader();
|
|||
|
|
reader.onload = (e) => {
|
|||
|
|
fabric.Image.fromURL(
|
|||
|
|
e.target.result,
|
|||
|
|
(img) => {
|
|||
|
|
if (!img) {
|
|||
|
|
reject(new Error("无法创建图像对象"));
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置图像基本属性
|
|||
|
|
const imageId = `image_${Date.now()}_${Math.floor(
|
|||
|
|
Math.random() * 1000
|
|||
|
|
)}`;
|
|||
|
|
|
|||
|
|
// 获取画布中心点
|
|||
|
|
const canvasCenter = this.canvas.getCenter();
|
|||
|
|
|
|||
|
|
img.set({
|
|||
|
|
id: imageId,
|
|||
|
|
left: canvasCenter.left,
|
|||
|
|
top: canvasCenter.top,
|
|||
|
|
originX: "center",
|
|||
|
|
originY: "center",
|
|||
|
|
selectable: true,
|
|||
|
|
evented: true,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
resolve(img);
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
crossOrigin: "anonymous",
|
|||
|
|
}
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
reader.onerror = () => reject(new Error("文件读取失败"));
|
|||
|
|
reader.readAsDataURL(file);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 确定目标图层
|
|||
|
|
* @returns {Promise<Object>} 目标图层对象
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
async _determineTargetLayer() {
|
|||
|
|
// 如果指定了目标图层ID,查找该图层
|
|||
|
|
if (this.targetLayerId) {
|
|||
|
|
const existingLayer = this.layers.value.find(
|
|||
|
|
(layer) => layer.id === this.targetLayerId
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
if (existingLayer) {
|
|||
|
|
// 验证图层是否可以添加对象
|
|||
|
|
if (existingLayer.isBackground) {
|
|||
|
|
throw new Error("不能向背景图层添加对象");
|
|||
|
|
}
|
|||
|
|
if (existingLayer.locked) {
|
|||
|
|
throw new Error("不能向锁定图层添加对象");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log(`使用指定图层: ${existingLayer.name}`);
|
|||
|
|
return existingLayer;
|
|||
|
|
} else {
|
|||
|
|
console.warn(`指定的图层ID不存在: ${this.targetLayerId},将创建新图层`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建新图层
|
|||
|
|
return await this._createNewLayer();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 创建新图层
|
|||
|
|
* @returns {Promise<Object>} 新创建的图层对象
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
async _createNewLayer() {
|
|||
|
|
// 生成图层名称
|
|||
|
|
const fileName = this.imageFile.name.replace(/\.[^/.]+$/, ""); // 移除文件扩展名
|
|||
|
|
const layerName = fileName || `图片 ${new Date().toLocaleTimeString()}`;
|
|||
|
|
|
|||
|
|
// 创建图层对象
|
|||
|
|
const newLayer = createLayer({
|
|||
|
|
name: layerName,
|
|||
|
|
type: LayerType.BITMAP,
|
|||
|
|
visible: true,
|
|||
|
|
locked: false,
|
|||
|
|
opacity: 1.0,
|
|||
|
|
fabricObjects: [],
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 执行添加图层命令
|
|||
|
|
const addLayerCmd = new AddLayerCommand({
|
|||
|
|
canvas: this.canvas,
|
|||
|
|
layers: this.layers,
|
|||
|
|
newLayer: newLayer,
|
|||
|
|
activeLayerId: this.activeLayerId,
|
|||
|
|
insertIndex: this.layerManager?._getInsertIndexAboveActiveLayer?.(),
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const layerId = await addLayerCmd.execute();
|
|||
|
|
this.subCommands.push(addLayerCmd);
|
|||
|
|
this.executedSubCommands.push(addLayerCmd);
|
|||
|
|
this.wasLayerCreated = true;
|
|||
|
|
|
|||
|
|
console.log(`创建新图层: ${layerName}, ID: ${layerId}`);
|
|||
|
|
return newLayer;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 添加图像到图层
|
|||
|
|
* @param {Object} targetLayer 目标图层
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
async _addImageToLayer(targetLayer) {
|
|||
|
|
// 设置图像与图层的关联
|
|||
|
|
this.createdImageObject.set({
|
|||
|
|
layerId: targetLayer.id,
|
|||
|
|
layerName: targetLayer.name,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 执行添加对象到图层命令
|
|||
|
|
const addObjectCmd = new AddObjectToLayerCommand({
|
|||
|
|
canvas: this.canvas,
|
|||
|
|
layers: this.layers,
|
|||
|
|
layerId: targetLayer.id,
|
|||
|
|
fabricObject: this.createdImageObject,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
await addObjectCmd.execute();
|
|||
|
|
this.subCommands.push(addObjectCmd);
|
|||
|
|
this.executedSubCommands.push(addObjectCmd);
|
|||
|
|
|
|||
|
|
console.log(`图像已添加到图层: ${targetLayer.name}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 切换到选择工具
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
async _switchToSelectTool() {
|
|||
|
|
if (!this.toolManager) return;
|
|||
|
|
|
|||
|
|
const toolCmd = new ToolCommand({
|
|||
|
|
toolManager: this.toolManager,
|
|||
|
|
tool: OperationType.SELECT,
|
|||
|
|
previousTool: this.toolManager.getCurrentTool(),
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
await toolCmd.execute();
|
|||
|
|
this.subCommands.push(toolCmd);
|
|||
|
|
this.executedSubCommands.push(toolCmd);
|
|||
|
|
|
|||
|
|
console.log("已切换到选择工具");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 回滚已执行的子命令
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
async _rollbackSubCommands() {
|
|||
|
|
console.log(`🔄 开始回滚 ${this.executedSubCommands.length} 个子命令`);
|
|||
|
|
|
|||
|
|
const commands = [...this.executedSubCommands].reverse();
|
|||
|
|
|
|||
|
|
for (const command of commands) {
|
|||
|
|
if (typeof command.undo === "function") {
|
|||
|
|
try {
|
|||
|
|
console.log(`🔄 回滚子命令: ${command.constructor.name}`);
|
|||
|
|
const result = command.undo();
|
|||
|
|
if (this._isPromise(result)) {
|
|||
|
|
await result;
|
|||
|
|
}
|
|||
|
|
console.log(`✅ 子命令回滚成功: ${command.constructor.name}`);
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error(
|
|||
|
|
`❌ 子命令回滚失败: ${command.constructor.name}`,
|
|||
|
|
error
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.executedSubCommands = [];
|
|||
|
|
console.log(`✅ 回滚完成`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async undo() {
|
|||
|
|
if (this.executedCommands.length === 0) {
|
|||
|
|
console.warn("没有已执行的命令需要撤销");
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log(
|
|||
|
|
`↩️ 开始撤销创建图片图层操作,共 ${this.executedCommands.length} 个子命令`
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 逆序撤销已执行的命令
|
|||
|
|
const commands = [...this.executedCommands].reverse();
|
|||
|
|
|
|||
|
|
for (const command of commands) {
|
|||
|
|
if (typeof command.undo === "function") {
|
|||
|
|
try {
|
|||
|
|
console.log(`↩️ 撤销子命令: ${command.constructor.name}`);
|
|||
|
|
const result = command.undo();
|
|||
|
|
if (this._isPromise(result)) {
|
|||
|
|
await result;
|
|||
|
|
}
|
|||
|
|
console.log(`✅ 子命令撤销成功: ${command.constructor.name}`);
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error(
|
|||
|
|
`❌ 子命令撤销失败: ${command.constructor.name}`,
|
|||
|
|
error
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.executedCommands = [];
|
|||
|
|
console.log(`✅ 创建图片图层撤销完成`);
|
|||
|
|
return true;
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error("❌ 撤销创建图片图层过程中发生错误:", error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 检查返回值是否为Promise
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
_isPromise(value) {
|
|||
|
|
return (
|
|||
|
|
value &&
|
|||
|
|
typeof value === "object" &&
|
|||
|
|
typeof value.then === "function" &&
|
|||
|
|
typeof value.catch === "function"
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
getInfo() {
|
|||
|
|
return {
|
|||
|
|
name: this.name,
|
|||
|
|
layerName: this.layerName,
|
|||
|
|
layerId: this.newLayerId,
|
|||
|
|
commandCount: this.commands.length,
|
|||
|
|
executedCount: this.executedCommands.length,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|