feat: 液化撤销问题修复+选取更改逻辑+右键删除组图层问题修复
This commit is contained in:
@@ -5,7 +5,10 @@ import {
|
||||
} from "../utils/layerHelper.js";
|
||||
import { createRasterizedImage } from "../utils/selectionToImage.js";
|
||||
import { CompositeCommand, Command } from "./Command.js";
|
||||
import { CreateImageLayerCommand } from "./LayerCommands.js";
|
||||
import {
|
||||
CreateImageLayerCommand,
|
||||
RemoveLayerCommand,
|
||||
} from "./LayerCommands.js";
|
||||
import { fabric } from "fabric-with-all";
|
||||
import { generateId } from "../utils/helper.js";
|
||||
|
||||
@@ -41,6 +44,13 @@ export class LassoCutoutCommand extends CompositeCommand {
|
||||
// 在初始化时克隆保存选区对象,避免撤销后重做时获取不到选区对象
|
||||
this._clonedSelectionObject = null;
|
||||
this._initializeClonedSelection();
|
||||
|
||||
// 新增:保存原图层信息用于撤销恢复
|
||||
this.originalLayer = null; // 保存原图层的完整信息
|
||||
this.originalLayerIndex = -1; // 保存原图层在layers数组中的索引
|
||||
this.originalFabricObjects = []; // 保存原图层的所有fabric对象(序列化)
|
||||
this.originalCanvasObjects = []; // 保存从画布中获取的真实fabric对象
|
||||
this._initializeOriginalLayerInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,6 +80,43 @@ export class LassoCutoutCommand extends CompositeCommand {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化原图层信息
|
||||
* @private
|
||||
*/
|
||||
_initializeOriginalLayerInfo() {
|
||||
if (this.layerManager) {
|
||||
const activeLayer = this.layerManager.getActiveLayer();
|
||||
if (activeLayer) {
|
||||
// 保存原图层的完整信息
|
||||
this.originalLayer = JSON.parse(JSON.stringify(activeLayer)); // 深拷贝
|
||||
|
||||
// 保存原图层在layers数组中的索引
|
||||
this.originalLayerIndex = this.layerManager.layers.value.findIndex(
|
||||
(layer) => layer.id === activeLayer.id
|
||||
);
|
||||
|
||||
// 获取并序列化原图层的所有fabric对象
|
||||
const sourceObjects = this._getLayerObjects(activeLayer);
|
||||
this.originalCanvasObjects = sourceObjects; // 保存真实对象引用
|
||||
this.originalFabricObjects = sourceObjects.map((obj) =>
|
||||
obj.toObject([
|
||||
"id",
|
||||
"layerId",
|
||||
"layerName",
|
||||
"parentId",
|
||||
"type",
|
||||
"custom",
|
||||
])
|
||||
);
|
||||
|
||||
console.log(
|
||||
`套索抠图:已保存原图层信息,图层名: ${activeLayer.name},对象数量: ${sourceObjects.length}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async execute() {
|
||||
if (!this.canvas || !this.layerManager || !this.selectionManager) {
|
||||
console.error("无法执行套索抠图:参数无效");
|
||||
@@ -146,7 +193,19 @@ export class LassoCutoutCommand extends CompositeCommand {
|
||||
this.newLayerId = createImageLayerCmd.newLayerId;
|
||||
this.executedCommands.push(createImageLayerCmd);
|
||||
|
||||
// 2. 清除选区命令
|
||||
// 2. 删除原图层命令
|
||||
const removeOriginalLayerCmd = new RemoveLayerCommand({
|
||||
canvas: this.canvas,
|
||||
layers: this.layerManager.layers,
|
||||
layerId: this.originalLayer.id,
|
||||
activeLayerId: this.layerManager.activeLayerId,
|
||||
});
|
||||
|
||||
// 执行删除原图层命令
|
||||
await removeOriginalLayerCmd.execute();
|
||||
this.executedCommands.push(removeOriginalLayerCmd);
|
||||
|
||||
// 3. 清除选区命令
|
||||
const clearSelectionCmd = new ClearSelectionCommand({
|
||||
canvas: this.canvas,
|
||||
selectionManager: this.selectionManager,
|
||||
@@ -276,6 +335,7 @@ export class LassoCutoutCommand extends CompositeCommand {
|
||||
}
|
||||
|
||||
// 2. 逆序撤销所有已执行的子命令
|
||||
// RemoveLayerCommand的undo会自动恢复原图层,所以不需要再调用_restoreOriginalLayer
|
||||
for (let i = this.executedCommands.length - 1; i >= 0; i--) {
|
||||
const command = this.executedCommands[i];
|
||||
if (command && typeof command.undo === "function") {
|
||||
@@ -293,6 +353,12 @@ export class LassoCutoutCommand extends CompositeCommand {
|
||||
}
|
||||
}
|
||||
|
||||
// 注意:不需要调用_restoreOriginalLayer,因为RemoveLayerCommand的undo已经恢复了原图层
|
||||
// 但是需要确保画布状态正确,所以手动触发一次渲染
|
||||
if (this.canvas) {
|
||||
this.canvas.renderAll();
|
||||
}
|
||||
|
||||
// 3. 清理状态
|
||||
this.executedCommands = [];
|
||||
this.newLayerId = null;
|
||||
@@ -331,6 +397,13 @@ export class LassoCutoutCommand extends CompositeCommand {
|
||||
baseResolutionScale: this.baseResolutionScale,
|
||||
hasSerializedSelection: !!this.serializedSelectionObject,
|
||||
selectionType: this.serializedSelectionObject?.type || null,
|
||||
// 新增:原图层信息
|
||||
hasOriginalLayer: !!this.originalLayer,
|
||||
originalLayerName: this.originalLayer?.name || null,
|
||||
originalLayerId: this.originalLayer?.id || null,
|
||||
originalLayerIndex: this.originalLayerIndex,
|
||||
originalFabricObjectsCount: this.originalFabricObjects.length,
|
||||
originalCanvasObjectsCount: this.originalCanvasObjects.length,
|
||||
subCommands: this.executedCommands.map((cmd) => ({
|
||||
name: cmd.constructor.name,
|
||||
info: cmd.getInfo ? cmd.getInfo() : {},
|
||||
@@ -815,6 +888,109 @@ export class LassoCutoutCommand extends CompositeCommand {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复原图层和其fabric对象
|
||||
* @private
|
||||
*/
|
||||
async _restoreOriginalLayer() {
|
||||
if (!this.originalLayer) {
|
||||
console.warn("没有保存的原图层信息,无法恢复");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log(`↩️ 开始恢复原图层: ${this.originalLayer.name}`);
|
||||
|
||||
// 1. 恢复图层到原位置
|
||||
if (this.originalLayerIndex !== -1) {
|
||||
this.layerManager.layers.value.splice(
|
||||
this.originalLayerIndex,
|
||||
0,
|
||||
this.originalLayer
|
||||
);
|
||||
} else {
|
||||
// 如果没有保存索引,添加到末尾
|
||||
this.layerManager.layers.value.push(this.originalLayer);
|
||||
}
|
||||
|
||||
// 2. 恢复fabric对象到画布
|
||||
if (this.originalFabricObjects.length > 0) {
|
||||
console.log(
|
||||
`↩️ 恢复 ${this.originalFabricObjects.length} 个fabric对象`
|
||||
);
|
||||
|
||||
// 使用fabric.util.enlivenObjects批量反序列化对象
|
||||
await new Promise((resolve, reject) => {
|
||||
fabric.util.enlivenObjects(
|
||||
this.originalFabricObjects,
|
||||
(restoredObjects) => {
|
||||
if (!restoredObjects || restoredObjects.length === 0) {
|
||||
console.warn("没有成功反序列化任何对象");
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
// 将恢复的对象添加到画布
|
||||
restoredObjects.forEach((obj) => {
|
||||
if (obj) {
|
||||
// 确保对象的图层信息正确
|
||||
obj.layerId = this.originalLayer.id;
|
||||
obj.layerName = this.originalLayer.name;
|
||||
obj.setCoords();
|
||||
this.canvas.add(obj);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`✅ 成功恢复 ${restoredObjects.length} 个fabric对象`);
|
||||
resolve();
|
||||
},
|
||||
// 命名空间,用于自定义对象类型
|
||||
""
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// 3. 如果恢复的是活动图层,设置为当前活动图层
|
||||
if (this.originalLayer.id === this.layerManager.activeLayerId.value) {
|
||||
this.layerManager.activeLayerId.value = this.originalLayer.id;
|
||||
} else {
|
||||
// 如果当前没有活动图层,将恢复的图层设为活动图层
|
||||
if (!this.layerManager.activeLayerId.value) {
|
||||
this.layerManager.activeLayerId.value = this.originalLayer.id;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 重新渲染画布
|
||||
this.canvas.renderAll();
|
||||
|
||||
console.log(`✅ 原图层恢复完成: ${this.originalLayer.name}`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("❌ 恢复原图层失败:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重做操作
|
||||
* @returns {Promise<Object|boolean>} 执行结果
|
||||
*/
|
||||
async redo() {
|
||||
console.log(`🔄 开始重做套索抠图操作`);
|
||||
|
||||
// 重做操作等同于重新执行,但需要先清理一些状态
|
||||
this.executedCommands = [];
|
||||
this.newLayerId = null;
|
||||
this.cutoutImageUrl = null;
|
||||
this.fabricImage = null;
|
||||
this.groupLayer = null;
|
||||
|
||||
// 重新生成groupId确保唯一性(可选,也可以复用原来的)
|
||||
// this.groupId = `cutout-group-${Date.now()}`;
|
||||
|
||||
return await this.execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user