Files
aida_front/src/component/Canvas/CanvasEditor/commands/LassoCutoutCommand.js
2026-01-26 16:16:40 +08:00

1381 lines
43 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
createLayer,
findInChildLayers,
LayerType,
OperationType,
} from "../utils/layerHelper.js";
import { createRasterizedImage } from "../utils/selectionToImage.js";
import { CompositeCommand, Command } from "./Command.js";
import {
CreateImageLayerCommand,
RemoveLayerCommand,
} from "./LayerCommands.js";
import { fabric } from "fabric-with-all";
import { generateId } from "../utils/helper.js";
import { ToolCommand } from "./ToolCommands.js";
import i18n from "@/lang/index.ts";
const { t } = i18n.global;
/**
* 套索抠图命令
* 实现将选区内容到新图层遮罩
*/
export class LassoCutoutCommand extends CompositeCommand {
constructor(options = {}) {
super([], {
name: "套索抠图",
description: "将选区抠图到新图层",
});
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.selectionManager = options.selectionManager;
this.toolManager = options.toolManager;
this.sourceLayerId = options.sourceLayerId;
this.newLayerName = options.newLayerName || "抠图";
this.newLayerId = null;
this.cutoutImageUrl = null;
this.fabricImage = null;
this.executedCommands = [];
// 高清截图选项
this.highResolutionEnabled = options.highResolutionEnabled !== false; // 默认启用
this.baseResolutionScale = options.baseResolutionScale || 2; // 基础分辨率倍数
this.groupId = options.groupId || generateId("lasso-group-");
this.groupName = options.groupName || t(`Canvas.ConstituencyGroup`);
this.clippingMaskId = generateId("clipping-mask-");
this.groupLayer = null; // 新增:保存组图层的引用
this.originalLayersLength = 0; // 新增:保存原始图层数量
// 在初始化时克隆保存选区对象,避免撤销后重做时获取不到选区对象
this._clonedSelectionObject = null;
this._initializeClonedSelection();
// 新增:保存原图层信息用于撤销恢复
this.originalLayer = null; // 保存原图层的完整信息
this.originalLayerIndex = -1; // 保存原图层在layers数组中的索引
this.originalFabricObjects = []; // 保存原图层的所有fabric对象序列化
this.originalCanvasObjects = []; // 保存从画布中获取的真实fabric对象
this._initializeOriginalLayerInfo();
this.oldActiveLayerId = this.layerManager.activeLayerId.value; // 保存旧的活动图层ID
}
/**
* 初始化克隆的选区对象
* @private
*/
async _initializeClonedSelection() {
if (this.selectionManager) {
const selectionObject = this.selectionManager.getSelectionObject();
if (selectionObject) {
try {
this._clonedSelectionObject = await this._cloneObject(
selectionObject
);
console.log("套索抠图:选区对象已克隆保存");
} catch (error) {
console.error("套索抠图:克隆选区对象失败:", error);
// 备用方案:序列化保存
this.serializedSelectionObject = selectionObject.toObject([
"id",
"layerId",
"layerName",
"parentId",
]);
}
}
}
}
/**
* 初始化原图层信息
* @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("无法执行套索抠图:参数无效");
return false;
}
try {
this.executedCommands = [];
// 保存原始图层数量,用于撤销时的验证
this.originalLayersLength = this.layerManager.layers.value.length;
// 获取选区:优先使用克隆的选区对象
const selectionObject = await this._getSelectionObject();
if (!selectionObject) {
console.error("无法执行套索抠图:当前没有选区");
return false;
}
// 确定源图层
// const sourceLayer = this.layerManager.getActiveLayer();
// if (!sourceLayer) {
// console.error("无法执行套索抠图:源图层无效");
// return false;
// }
// 获取源图层的所有对象(包括子图层)
// const sourceObjects = this._getLayerObjects(sourceLayer);
// if (sourceObjects.length === 0) {
// console.error("无法执行套索抠图:源图层没有可见对象");
// return false;
// }
const clippingMask = fabric.util.object.clone(selectionObject);
clippingMask.set({
id: this.clippingMaskId,
selectable: false,
evented: false,
hasControls: false,
// layerId: this.groupId,
visible: true,
});
// 获取选区边界信息用于后续定位
// const selectionBounds = selectionObject.getBoundingRect(true, true);
// 使用createRasterizedImage执行抠图操作
// this.cutoutImageUrl = await this._performCutoutWithRasterized(
// sourceObjects,
// selectionObject,
// selectionBounds
// );
// if (!this.cutoutImageUrl) {
// console.error("抠图失败");
// return false;
// }
// this.fabricImage = await this._performCutoutWithRasterized(
// sourceObjects,
// selectionObject,
// selectionBounds
// );
// // 创建fabric图像对象传递选区边界信息
// this.fabricImage = await this._createFabricImage(
// this.cutoutImageUrl,
// selectionBounds
// );
// if (!this.fabricImage) {
// console.error("创建图像对象失败");
// return false;
// }
// 1. 创建图像图层命令
// const createImageLayerCmd = new CreateImageLayerCommand({
// layerManager: this.layerManager,
// fabricImage: this.fabricImage,
// toolManager: this.toolManager,
// layerName: this.newLayerName,
// });
// // 执行创建图像图层命令
// const result = await createImageLayerCmd.execute();
// this.newLayerId = createImageLayerCmd.newLayerId;
// this.executedCommands.push(createImageLayerCmd);
// 3. 清除选区命令
const clearSelectionCmd = new ClearSelectionCommand({
canvas: this.canvas,
selectionManager: this.selectionManager,
});
// 执行清除选区命令
await clearSelectionCmd.execute();
this.executedCommands.push(clearSelectionCmd);
const layers = this.layerManager.layers.value;
var topLayerIndex = 0;
if(this.originalLayer)layers.forEach((layer, index) => {
if (layer.id === this.originalLayer.id) {
topLayerIndex = index;
}else if (layer.children.length > 0) {
layer.children.forEach((childLayer) => {
if (childLayer.id === this.originalLayer.id) {
topLayerIndex = index;
}
});
}
});
// const selectLayer = this.layerManager.layers.value[topLayerIndex];
// 创建新的组图层
this.groupLayer = createLayer({
id: this.groupId,
name: this.groupName || t(`Canvas.ConstituencyGroup`),
type: LayerType.GROUP,
visible: true,
locked: false,
opacity: 1.0,
fabricObjects: [],
children: [],
});
// this.fabricImage.set({
// selectable: true,
// evented: true,
// });
const selectLayer = createLayer({
name: t(`Canvas.ConstituencyEmptyLayer`),
type: LayerType.EMPTY,
visible: true,
locked: false,
opacity: 1.0,
fabricObjects: [],
children: [],
});
selectLayer.parentId = this.groupId; // 设置新图层的parentId为组图层ID
// selectLayer.fabricObjects = [
// this.fabricImage.toObject("id", "layerId", "layerName", "parentId"),
// ];
// 2. 删除原图层命令
// const removeOriginalLayerCmd = new RemoveLayerCommand({
// canvas: this.canvas,
// layers: this.layerManager.layers,
// layerId: this.originalLayer.id,
// activeLayerId: this.layerManager.activeLayerId,
// layerManager: this.layerManager,
// });
// // 执行删除原图层命令
// await removeOriginalLayerCmd.execute();
// this.executedCommands.push(removeOriginalLayerCmd);
this.groupLayer.clippingMask = clippingMask.toObject(["id", "layerId"]); // 设置组图层的fabricObject为遮罩图像
selectionObject.set({
id: generateId("selectionObject-"),
customType: "selectionObject",
});
this.groupLayer.selectObject = selectionObject.toObject([
"id",
"customType",
]);
this.groupLayer.children.push(selectLayer);
// 插入新组图层
console.log("新增套索添加index", topLayerIndex);
this.layerManager.layers.value.splice(topLayerIndex, 0, this.groupLayer);
this.layerManager.activeLayerId.value = selectLayer.id; // 设置新组图层为活动图层
// 切换工具到选择模式
// 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);
}
this.canvas.discardActiveObject();
// this.canvas.setActiveObject(this.fabricImage);
await this.layerManager.updateLayersObjectsInteractivity(true);
console.log(`套索抠图完成新图层ID: ${this.newLayerId}`);
return {
newLayerId: this.newLayerId,
cutoutImageUrl: this.cutoutImageUrl,
guroupId: this.groupId,
groupName: this.groupName,
};
} catch (error) {
console.error("套索抠图过程中出错:", error);
// 如果已经创建了新图层,需要进行清理
if (this.newLayerId) {
try {
await this.layerManager.removeLayer(this.newLayerId);
} catch (cleanupError) {
console.warn("清理新图层失败:", cleanupError);
}
}
// 清理组图层(如果已创建)
if (this.groupLayer && this.groupId) {
try {
const groupIndex = this.layerManager.layers.value.findIndex(
(layer) => layer.id === this.groupId
);
if (groupIndex !== -1) {
this.layerManager.layers.value.splice(groupIndex, 1);
console.log(`清理了异常创建的组图层: ${this.groupId}`);
}
this.groupLayer = null;
} catch (cleanupError) {
console.warn("清理组图层失败:", cleanupError);
}
}
// 尝试回滚已执行的命令
if (this.executedCommands.length > 0) {
try {
for (let i = this.executedCommands.length - 1; i >= 0; i--) {
const command = this.executedCommands[i];
if (command && typeof command.undo === "function") {
await command.undo();
}
}
this.executedCommands = [];
} catch (rollbackError) {
console.warn("回滚已执行命令失败:", rollbackError);
}
}
throw error;
}
}
async undo() {
try {
console.log(`↩️ 开始撤销套索抠图操作`);
// 1. 首先移除组图层(如果存在)
if (this.groupId) {
const groupIndex = this.layerManager.layers.value.findIndex(
(layer) => layer.id === this.groupId
);
if (groupIndex !== -1) {
console.log(`↩️ 移除组图层: ${this.groupId}`);
// 从图层列表中移除组图层
this.layerManager.layers.value.splice(groupIndex, 1);
}
}
this.layerManager.activeLayerId.value = this.oldActiveLayerId; // 恢复旧的活动图层ID
if (this.fabricImage) {
console.log(`↩️ 移除抠图图像: ${this.fabricImage.id}`);
// 从画布中移除抠图图像
this.canvas.remove(this.fabricImage);
}
// 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") {
try {
console.log(`↩️ 撤销子命令: ${command.constructor.name}`);
await command.undo();
console.log(`✅ 子命令撤销成功: ${command.constructor.name}`);
} catch (error) {
console.error(
`❌ 子命令撤销失败: ${command.constructor.name}`,
error
);
// 子命令撤销失败不中断整个撤销过程
}
}
}
// 注意不需要调用_restoreOriginalLayer因为RemoveLayerCommand的undo已经恢复了原图层
// 但是需要确保画布状态正确,所以手动触发一次渲染
if (this.canvas) {
this.canvas.renderAll();
}
// 3. 清理状态
this.executedCommands = [];
this.newLayerId = null;
this.cutoutImageUrl = null;
this.fabricImage = null;
this.groupLayer = null; // 清理组图层引用
// 注意不重置groupId因为重做时可能需要使用相同的ID
// 4. 更新画布和图层交互性
await this.layerManager.updateLayersObjectsInteractivity(true);
console.log(`✅ 套索抠图撤销完成`);
return true;
} catch (error) {
console.error("❌ 撤销套索抠图失败:", error);
return false;
}
}
/**
* 获取命令信息
* @returns {Object} 命令详细信息
*/
getInfo() {
return {
name: this.name,
description: this.description,
newLayerId: this.newLayerId,
newLayerName: this.newLayerName,
groupId: this.groupId,
groupName: this.groupName,
executedCommandsCount: this.executedCommands.length,
hasGroupLayer: !!this.groupLayer,
sourceLayerId: this.sourceLayerId,
highResolutionEnabled: this.highResolutionEnabled,
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() : {},
})),
};
}
/**
* 获取图层的所有对象(包括子图层,从画布中查找真实对象)
* @param {Object} layer 图层对象
* @returns {Array} 真实的fabric对象数组
* @private
*/
_getLayerObjects(layer) {
const objects = [];
const canvasObjects = this.canvas.getObjects();
// 递归获取图层及其子图层的所有对象
const collectLayerObjects = (currentLayer) => {
// 处理图层的fabricObjects
if (
currentLayer.fabricObjects &&
Array.isArray(currentLayer.fabricObjects)
) {
currentLayer.fabricObjects.forEach((fabricRef) => {
if (fabricRef && fabricRef.id) {
// 从画布中查找真实的对象
const realObject = canvasObjects.find(
(obj) => obj.id === fabricRef.id
);
if (realObject && realObject.visible) {
objects.push(realObject);
}
}
});
}
// 处理单个fabricObject背景图层等
if (currentLayer.fabricObject && currentLayer.fabricObject.id) {
const realObject = canvasObjects.find(
(obj) => obj.id === currentLayer.fabricObject.id
);
if (realObject && realObject.visible) {
objects.push(realObject);
}
}
// 递归处理子图层
if (currentLayer.children && Array.isArray(currentLayer.children)) {
currentLayer.children.forEach((childLayer) => {
if (childLayer.visible !== false) {
// 只处理可见的子图层
collectLayerObjects(childLayer);
}
});
}
};
collectLayerObjects(layer);
console.log(`从图层 "${layer.name}" 收集到 ${objects.length} 个可见对象`);
return objects;
}
/**
* 使用createRasterizedImage执行抠图操作
* @param {Array} sourceObjects 源对象数组
* @param {Object} selectionObject 选区对象
* @param {Object} selectionBounds 选区边界
* @returns {String} 抠图结果的DataURL
* @private
*/
async _performCutoutWithRasterized(
sourceObjects,
selectionObject,
selectionBounds
) {
try {
console.log("=== 开始使用createRasterizedImage执行抠图 ===");
console.log(`源对象数量: ${sourceObjects.length}`);
console.log(`选区边界:`, selectionBounds);
// 确定缩放因子,确保高质量输出
let scaleFactor = this.baseResolutionScale;
if (this.highResolutionEnabled) {
const devicePixelRatio = window.devicePixelRatio || 1;
scaleFactor = Math.max(scaleFactor, devicePixelRatio * 1.5);
}
// 使用createRasterizedImage生成栅格化图像将选区作为裁剪路径
const rasterizedDataURL = await createRasterizedImage({
canvas: this.canvas,
fabricObjects: sourceObjects,
clipPath: selectionObject, // 使用选区作为裁剪路径
trimWhitespace: true,
trimPadding: 2,
quality: 1.0,
format: "png",
scaleFactor: scaleFactor,
// isReturenDataURL: true, // 返回DataURL
preserveOriginalQuality: true, // 启用高质量模式
selectionManager: this.selectionManager, // 传递选区管理器,用于获取羽化值
});
if (!rasterizedDataURL) {
throw new Error("栅格化生成图像失败");
}
console.log(`✅ 高质量抠图完成,缩放因子: ${scaleFactor}x`);
return rasterizedDataURL;
} catch (error) {
console.error("使用createRasterizedImage执行抠图失败:", error);
// 如果createRasterizedImage失败回退到原始方法
console.log("⚠️ 回退到原始抠图方法...");
return await this._performCutout(
{ fabricObjects: sourceObjects },
selectionObject
);
}
}
/**
* 原始抠图方法(作为备用方案)
* @param {Object} sourceLayer 源图层
* @param {Object} selectionObject 选区对象
* @returns {String} 抠图结果的DataURL
* @private
*/
async _performCutout(sourceLayer, selectionObject) {
try {
console.log("=== 使用原始方法执行抠图 ===");
// 获取选区边界
const selectionBounds = selectionObject.getBoundingRect(true, true);
// 保存画布当前状态
const originalActiveObject = this.canvas.getActiveObject();
const originalSelection = this.canvas.selection;
// 临时禁用画布选择
this.canvas.selection = false;
this.canvas.discardActiveObject();
let tempGroup = null;
let originalObjects = [];
try {
// 收集源图层中的可见对象
const visibleObjects = sourceLayer.fabricObjects.filter(
(obj) => obj.visible
);
if (visibleObjects.length === 0) {
throw new Error("源图层没有可见对象");
}
// 如果只有一个对象且已经是组,直接使用
if (visibleObjects.length === 1 && visibleObjects[0].type === "group") {
tempGroup = visibleObjects[0];
} else {
// 创建临时组
const clonedObjects = [];
for (const obj of visibleObjects) {
const cloned = await this._cloneObject(obj);
clonedObjects.push(cloned);
}
tempGroup = new fabric.Group(clonedObjects, {
selectable: false,
evented: false,
});
this.canvas.add(tempGroup);
}
// 设置选区为裁剪路径
const clipPath = await this._cloneObject(selectionObject);
clipPath.set({
fill: "",
stroke: "",
absolutePositioned: true,
originX: "left",
originY: "top",
});
tempGroup.set({
clipPath: clipPath,
});
this.canvas.renderAll();
// 计算渲染区域
const renderBounds = {
left: selectionBounds.left,
top: selectionBounds.top,
width: selectionBounds.width,
height: selectionBounds.height,
};
// 设置高分辨率倍数
let highResolutionScale = 1;
if (this.highResolutionEnabled) {
const devicePixelRatio = window.devicePixelRatio || 1;
highResolutionScale = Math.max(
this.baseResolutionScale,
devicePixelRatio * 2
);
}
// 创建用于导出的高分辨率canvas
const exportCanvas = document.createElement("canvas");
const exportCtx = exportCanvas.getContext("2d", {
alpha: true,
willReadFrequently: false,
colorSpace: "srgb",
});
const actualWidth = Math.round(
renderBounds.width * highResolutionScale
);
const actualHeight = Math.round(
renderBounds.height * highResolutionScale
);
exportCanvas.width = actualWidth;
exportCanvas.height = actualHeight;
exportCanvas.style.width = renderBounds.width + "px";
exportCanvas.style.height = renderBounds.height + "px";
exportCtx.imageSmoothingEnabled = true;
exportCtx.imageSmoothingQuality = "high";
exportCtx.clearRect(0, 0, actualWidth, actualHeight);
const vpt = this.canvas.viewportTransform;
const zoom = this.canvas.getZoom();
exportCtx.save();
exportCtx.scale(highResolutionScale, highResolutionScale);
exportCtx.translate(-renderBounds.left, -renderBounds.top);
if (zoom !== 1 || vpt[4] !== 0 || vpt[5] !== 0) {
exportCtx.transform(vpt[0], vpt[1], vpt[2], vpt[3], vpt[4], vpt[5]);
}
tempGroup.render(exportCtx);
exportCtx.restore();
const dataUrl = exportCanvas.toDataURL("image/png", 1.0);
return dataUrl;
} finally {
// 清理和恢复
if (tempGroup) {
tempGroup.set({ clipPath: null });
if (originalObjects.length > 0) {
this.canvas.remove(tempGroup);
}
}
this.canvas.selection = originalSelection;
if (originalActiveObject) {
this.canvas.setActiveObject(originalActiveObject);
}
this.canvas.renderAll();
}
} catch (error) {
console.error("原始方法执行抠图失败:", error);
return null;
}
}
/**
* 从DataURL创建fabric图像对象
* @param {String} dataUrl 图像DataURL
* @param {Object} selectionBounds 选区边界信息
* @returns {fabric.Image} fabric图像对象
* @private
*/
async _createFabricImage(dataUrl, selectionBounds) {
return new Promise((resolve, reject) => {
fabric.Image.fromURL(
dataUrl,
(img) => {
if (!img) {
reject(new Error("无法从DataURL创建图像"));
return;
}
// 使用选区的位置作为图像位置
let targetLeft = selectionBounds.left + selectionBounds.width / 2;
let targetTop = selectionBounds.top + selectionBounds.height / 2;
// 设置图像属性,保持选区的原始尺寸
img.set({
left: targetLeft,
top: targetTop,
originX: "center",
originY: "center",
selectable: true,
evented: true,
hasControls: true,
hasBorders: true,
cornerStyle: "circle",
cornerColor: "#007aff",
cornerSize: 10,
transparentCorners: false,
borderColor: "#007aff",
borderScaleFactor: 2,
// 优化图像渲染质量
objectCaching: false, // 禁用缓存以确保最佳质量
statefullCache: true,
noScaleCache: false,
});
// 更新坐标
img.setCoords();
console.log(
`图像创建完成,位置: (${targetLeft}, ${targetTop}), 尺寸: ${img.width}x${img.height}`
);
resolve(img);
},
{
crossOrigin: "anonymous",
// 确保图像以最高质量加载
quality: 1.0,
}
);
});
}
/**
* 克隆fabric对象
* @param {Object} obj fabric对象
* @returns {Object} 克隆的对象
* @private
*/
async _cloneObject(obj) {
return new Promise((resolve, reject) => {
if (!obj) {
reject(new Error("对象无效,无法克隆"));
return;
}
try {
obj.clone((cloned) => {
if (cloned) {
resolve(cloned);
} else {
reject(new Error("对象克隆失败"));
}
});
} catch (error) {
reject(error);
}
});
}
/**
* 序列化选区对象
* @private
*/
_serializeSelectionObject() {
try {
if (!this.selectionManager) {
console.warn("选区管理器不存在,无法序列化选区对象");
return;
}
const selectionObject = this.selectionManager.getSelectionObject();
if (!selectionObject) {
console.warn("当前没有选区对象,无法序列化");
return;
}
// 将选区对象转换为可序列化的对象
this.serializedSelectionObject = selectionObject.toObject([
"id",
"layerId",
"layerName",
"parentId",
]);
console.log("选区对象已序列化保存");
} catch (error) {
console.error("序列化选区对象失败:", error);
this.serializedSelectionObject = null;
}
}
/**
* 反序列化选区对象
* @returns {Promise<Object>} 选区对象
* @private
*/
async _getSelectionObject() {
try {
// 优先使用克隆的选区对象
if (this._clonedSelectionObject) {
console.log("使用克隆的选区对象");
return await this._cloneObject(this._clonedSelectionObject);
}
// 尝试从选区管理器获取当前选区
const currentSelection = this.selectionManager.getSelectionObject();
if (currentSelection) {
console.log("从选区管理器获取到当前选区");
return currentSelection;
}
// 最后使用序列化数据恢复(备用方案)
if (!this.serializedSelectionObject) {
console.error("没有可用的选区对象数据");
return null;
}
console.log("从序列化数据恢复选区对象");
// 根据选区对象类型进行反序列化
return new Promise((resolve, reject) => {
const objectType = this.serializedSelectionObject.type;
if (objectType === "path") {
fabric.Path.fromObject(this.serializedSelectionObject, (path) => {
if (path) {
console.log("路径选区对象反序列化成功");
resolve(path);
} else {
reject(new Error("路径选区对象反序列化失败"));
}
});
} else if (objectType === "polygon") {
fabric.Polygon.fromObject(
this.serializedSelectionObject,
(polygon) => {
if (polygon) {
console.log("多边形选区对象反序列化成功");
resolve(polygon);
} else {
reject(new Error("多边形选区对象反序列化失败"));
}
}
);
} else if (objectType === "rect") {
fabric.Rect.fromObject(this.serializedSelectionObject, (rect) => {
if (rect) {
console.log("矩形选区对象反序列化成功");
resolve(rect);
} else {
reject(new Error("矩形选区对象反序列化失败"));
}
});
} else if (objectType === "ellipse" || objectType === "circle") {
fabric.Ellipse.fromObject(
this.serializedSelectionObject,
(ellipse) => {
if (ellipse) {
console.log("椭圆选区对象反序列化成功");
resolve(ellipse);
} else {
reject(new Error("椭圆选区对象反序列化失败"));
}
}
);
} else {
// 通用对象反序列化
fabric.util.enlivenObjects(
[this.serializedSelectionObject],
(objects) => {
if (objects && objects.length > 0) {
console.log("通用选区对象反序列化成功");
resolve(objects[0]);
} else {
reject(new Error("通用选区对象反序列化失败"));
}
}
);
}
});
} catch (error) {
console.error("获取选区对象失败:", error);
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();
}
}
/**
* 清除选区命令
*/
export class ClearSelectionCommand extends Command {
constructor(options = {}) {
super({
name: "清除选区",
description: "清除当前选区",
saveState: false,
});
this.selectionManager = options.selectionManager;
// 序列化保存完整的选区状态
this.originalSelectionState = null;
this._serializeSelectionState();
}
async execute() {
if (!this.selectionManager) {
console.error("无法清除选区:参数无效");
return false;
}
// 在执行前再次保存选区状态(以防初始化时没有选区)
if (!this.originalSelectionState) {
this._serializeSelectionState();
}
// 清除选区
this.selectionManager.clearSelection();
return true;
}
async undo() {
if (!this.selectionManager || !this.originalSelectionState) return false;
try {
// 从序列化状态恢复选区
await this._restoreSelectionState();
return true;
} catch (error) {
console.error("恢复选区状态失败:", error);
return false;
}
}
/**
* 重做操作与execute相同
* @returns {Promise<boolean>} 执行结果
*/
async redo() {
return await this.execute();
}
/**
* 序列化选区状态
* @private
*/
_serializeSelectionState() {
try {
if (!this.selectionManager) {
console.warn("选区管理器不存在,无法序列化选区状态");
return;
}
const selectionObject = this.selectionManager.getSelectionObject();
if (!selectionObject) {
console.log("当前没有选区对象,无需序列化");
return;
}
// 序列化选区对象和相关状态
this.originalSelectionState = {
// 选区对象的序列化数据
selectionObjectData: selectionObject.toObject([
"id",
"layerId",
"layerName",
"parentId",
]),
// 选区路径数据
selectionPath: this.selectionManager.getSelectionPath(),
// 羽化值
featherAmount: this.selectionManager.getFeatherAmount(),
// 选区ID
selectionId: selectionObject.id,
// 选区类型
selectionType: selectionObject.type,
// 选区在画布中的位置
position: {
left: selectionObject.left,
top: selectionObject.top,
originX: selectionObject.originX,
originY: selectionObject.originY,
},
// 选区样式
selectionStyle: {
stroke: selectionObject.stroke,
strokeWidth: selectionObject.strokeWidth,
strokeDashArray: selectionObject.strokeDashArray,
fill: selectionObject.fill,
shadow: selectionObject.shadow
? {
color: selectionObject.shadow.color,
blur: selectionObject.shadow.blur,
offsetX: selectionObject.shadow.offsetX,
offsetY: selectionObject.shadow.offsetY,
}
: null,
},
// 选区管理器的内部状态
managerState: {
isActive: this.selectionManager.isActive,
currentTool: this.selectionManager.currentTool,
selectionType: this.selectionManager.selectionType,
},
};
console.log("选区状态已序列化保存", {
selectionId: this.originalSelectionState.selectionId,
selectionType: this.originalSelectionState.selectionType,
featherAmount: this.originalSelectionState.featherAmount,
});
} catch (error) {
console.error("序列化选区状态失败:", error);
this.originalSelectionState = null;
}
}
/**
* 从序列化状态恢复选区
* @private
*/
async _restoreSelectionState() {
return new Promise((resolve, reject) => {
try {
if (!this.originalSelectionState) {
console.warn("没有保存的选区状态");
resolve();
return;
}
const {
selectionObjectData,
featherAmount,
selectionStyle,
position,
managerState,
} = this.originalSelectionState;
// 根据选区对象类型进行反序列化
const objectType = selectionObjectData.type;
const restoreSelection = (restoredObject) => {
if (!restoredObject) {
reject(new Error("选区对象反序列化失败"));
return;
}
try {
// 恢复选区ID和基本属性
restoredObject.set({
id: this.originalSelectionState.selectionId,
layerId: selectionObjectData.layerId,
layerName: selectionObjectData.layerName,
parentId: selectionObjectData.parentId,
});
// 恢复位置信息
if (position) {
restoredObject.set({
left: position.left,
top: position.top,
originX: position.originX || "left",
originY: position.originY || "top",
});
}
// 恢复样式
if (selectionStyle) {
restoredObject.set({
stroke: selectionStyle.stroke,
strokeWidth: selectionStyle.strokeWidth,
strokeDashArray: selectionStyle.strokeDashArray,
fill: selectionStyle.fill,
selectable: false,
evented: false,
excludeFromExport: true,
hoverCursor: "default",
moveCursor: "default",
});
// 恢复阴影(羽化效果)
if (selectionStyle.shadow) {
restoredObject.shadow = new fabric.Shadow(
selectionStyle.shadow
);
}
}
// 确保对象坐标正确设置
restoredObject.setCoords();
// 通过选区管理器设置选区对象,这会正确更新内部状态
this.selectionManager.setSelectionObject(restoredObject);
// 恢复羽化值
this.selectionManager.setFeatherAmount(featherAmount || 0);
// 恢复选区管理器的内部状态
if (managerState) {
// 注意不要强制设置isActive状态因为这可能影响工具切换
// this.selectionManager.isActive = managerState.isActive;
this.selectionManager.currentTool = managerState.currentTool;
this.selectionManager.selectionType = managerState.selectionType;
}
// 触发选区变化回调
if (
this.selectionManager.onSelectionChanged &&
typeof this.selectionManager.onSelectionChanged === "function"
) {
this.selectionManager.onSelectionChanged();
}
console.log("选区状态恢复成功", {
id: restoredObject.id,
type: restoredObject.type,
featherAmount: featherAmount,
});
resolve();
} catch (error) {
console.error("设置恢复的选区对象失败:", error);
reject(error);
}
};
// 根据对象类型进行反序列化
if (objectType === "path") {
fabric.Path.fromObject(selectionObjectData, restoreSelection);
} else if (objectType === "polygon") {
fabric.Polygon.fromObject(selectionObjectData, restoreSelection);
} else if (objectType === "rect") {
fabric.Rect.fromObject(selectionObjectData, restoreSelection);
} else if (objectType === "ellipse" || objectType === "circle") {
fabric.Ellipse.fromObject(selectionObjectData, restoreSelection);
} else {
// 通用对象反序列化
fabric.util.enlivenObjects([selectionObjectData], (objects) => {
if (objects && objects.length > 0) {
restoreSelection(objects[0]);
} else {
reject(new Error("通用选区对象反序列化失败"));
}
});
}
} catch (error) {
console.error("恢复选区状态失败:", error);
reject(error);
}
});
}
/**
* 验证选区状态是否正确恢复
* @private
*/
_validateRestoredState() {
if (!this.selectionManager || !this.originalSelectionState) {
return false;
}
const currentSelection = this.selectionManager.getSelectionObject();
if (!currentSelection) {
console.warn("选区对象未正确恢复");
return false;
}
// 验证基本属性
const originalId = this.originalSelectionState.selectionId;
if (currentSelection.id !== originalId) {
console.warn(
`选区ID不匹配: 期望 ${originalId}, 实际 ${currentSelection.id}`
);
return false;
}
// 验证羽化值
const currentFeather = this.selectionManager.getFeatherAmount();
const originalFeather = this.originalSelectionState.featherAmount;
if (currentFeather !== originalFeather) {
console.warn(
`羽化值不匹配: 期望 ${originalFeather}, 实际 ${currentFeather}`
);
return false;
}
console.log("选区状态验证通过");
return true;
}
/**
* 获取命令信息
* @returns {Object} 命令详细信息
*/
getInfo() {
return {
name: this.name,
description: this.description,
hasOriginalState: !!this.originalSelectionState,
selectionType: this.originalSelectionState?.selectionType || null,
featherAmount: this.originalSelectionState?.featherAmount || 0,
selectionId: this.originalSelectionState?.selectionId || null,
managerState: this.originalSelectionState?.managerState || null,
canUndo: !!this.originalSelectionState,
canRedo: true,
};
}
}