Files
aida_front/src/component/Canvas/CanvasEditor/commands/LassoCutoutCommand.js

1381 lines
43 KiB
JavaScript
Raw Normal View History

2025-07-21 01:17:25 +08:00
import {
createLayer,
findInChildLayers,
LayerType,
OperationType,
} from "../utils/layerHelper.js";
import { createRasterizedImage } from "../utils/selectionToImage.js";
import { CompositeCommand, Command } from "./Command.js";
2025-07-21 01:17:25 +08:00
import {
CreateImageLayerCommand,
RemoveLayerCommand,
} from "./LayerCommands.js";
2025-06-18 11:05:23 +08:00
import { fabric } from "fabric-with-all";
import { generateId } from "../utils/helper.js";
import { ToolCommand } from "./ToolCommands.js";
2025-09-24 16:26:40 +08:00
import i18n from "@/lang/index.ts";
const { t } = i18n.global;
2025-06-09 10:25:54 +08:00
/**
* 套索抠图命令
* 实现将选区内容到新图层遮罩
2025-06-09 10:25:54 +08:00
*/
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; // 默认启用
2025-06-23 00:40:45 +08:00
this.baseResolutionScale = options.baseResolutionScale || 2; // 基础分辨率倍数
2025-06-29 23:29:47 +08:00
this.groupId = options.groupId || generateId("lasso-group-");
2025-09-24 16:26:40 +08:00
this.groupName = options.groupName || t(`Canvas.ConstituencyGroup`);
this.clippingMaskId = generateId("clipping-mask-");
2025-06-29 23:29:47 +08:00
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 {
2025-07-21 01:17:25 +08:00
this._clonedSelectionObject = await this._cloneObject(
selectionObject
);
console.log("套索抠图:选区对象已克隆保存");
} catch (error) {
console.error("套索抠图:克隆选区对象失败:", error);
// 备用方案:序列化保存
this.serializedSelectionObject = selectionObject.toObject([
"id",
"layerId",
"layerName",
"parentId",
]);
}
}
}
2025-06-09 10:25:54 +08:00
}
/**
* 初始化原图层信息
* @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) =>
2025-07-21 01:17:25 +08:00
obj.toObject([
"id",
"layerId",
"layerName",
"parentId",
"type",
"custom",
])
);
console.log(
`套索抠图:已保存原图层信息,图层名: ${activeLayer.name},对象数量: ${sourceObjects.length}`
);
}
}
}
2025-06-09 10:25:54 +08:00
async execute() {
if (!this.canvas || !this.layerManager || !this.selectionManager) {
console.error("无法执行套索抠图:参数无效");
return false;
}
try {
this.executedCommands = [];
2025-06-29 23:29:47 +08:00
// 保存原始图层数量,用于撤销时的验证
this.originalLayersLength = this.layerManager.layers.value.length;
// 获取选区:优先使用克隆的选区对象
2025-06-29 23:29:47 +08:00
const selectionObject = await this._getSelectionObject();
2025-06-09 10:25:54 +08:00
if (!selectionObject) {
console.error("无法执行套索抠图:当前没有选区");
return false;
}
// 确定源图层
const sourceLayer = this.layerManager.getActiveLayer();
2025-06-23 00:40:45 +08:00
if (!sourceLayer) {
2025-06-09 10:25:54 +08:00
console.error("无法执行套索抠图:源图层无效");
return false;
}
2025-06-23 00:40:45 +08:00
// 获取源图层的所有对象(包括子图层)
// 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,
});
2025-06-23 00:40:45 +08:00
2025-06-09 10:25:54 +08:00
// 获取选区边界信息用于后续定位
// const selectionBounds = selectionObject.getBoundingRect(true, true);
2025-06-09 10:25:54 +08:00
2025-06-23 00:40:45 +08:00
// 使用createRasterizedImage执行抠图操作
// this.cutoutImageUrl = await this._performCutoutWithRasterized(
// sourceObjects,
// selectionObject,
// selectionBounds
// );
// if (!this.cutoutImageUrl) {
// console.error("抠图失败");
// return false;
// }
// this.fabricImage = await this._performCutoutWithRasterized(
// sourceObjects,
// selectionObject,
// selectionBounds
// );
2025-06-09 10:25:54 +08:00
// // 创建fabric图像对象传递选区边界信息
// this.fabricImage = await this._createFabricImage(
// this.cutoutImageUrl,
// selectionBounds
// );
// if (!this.fabricImage) {
// console.error("创建图像对象失败");
// return false;
// }
2025-06-09 10:25:54 +08:00
// 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. 清除选区命令
2025-06-09 10:25:54 +08:00
const clearSelectionCmd = new ClearSelectionCommand({
canvas: this.canvas,
selectionManager: this.selectionManager,
});
// 执行清除选区命令
await clearSelectionCmd.execute();
this.executedCommands.push(clearSelectionCmd);
2025-10-13 15:08:37 +08:00
const layers = this.layerManager.layers.value;
var topLayerIndex = 0;
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;
}
});
}
});
2025-06-29 23:29:47 +08:00
// const selectLayer = this.layerManager.layers.value[topLayerIndex];
2025-06-29 23:29:47 +08:00
// 创建新的组图层
this.groupLayer = createLayer({
id: this.groupId,
2025-09-24 16:26:40 +08:00
name: this.groupName || t(`Canvas.ConstituencyGroup`),
2025-06-29 23:29:47 +08:00
type: LayerType.GROUP,
visible: true,
locked: false,
opacity: 1.0,
fabricObjects: [],
children: [],
});
// this.fabricImage.set({
// selectable: true,
// evented: true,
// });
const selectLayer = createLayer({
2025-09-24 16:26:40 +08:00
name: t(`Canvas.ConstituencyEmptyLayer`),
type: LayerType.EMPTY,
visible: true,
locked: false,
opacity: 1.0,
fabricObjects: [],
children: [],
2025-06-29 23:29:47 +08:00
});
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,
2025-07-21 01:17:25 +08:00
// activeLayerId: this.layerManager.activeLayerId,
// layerManager: this.layerManager,
// });
// // 执行删除原图层命令
// await removeOriginalLayerCmd.execute();
// this.executedCommands.push(removeOriginalLayerCmd);
this.groupLayer.clippingMask = clippingMask.toObject(["id", "layerId"]); // 设置组图层的fabricObject为遮罩图像
2025-07-21 01:17:25 +08:00
selectionObject.set({
id: generateId("selectionObject-"),
customType: "selectionObject",
});
this.groupLayer.selectObject = selectionObject.toObject([
"id",
"customType",
]);
2025-06-29 23:29:47 +08:00
this.groupLayer.children.push(selectLayer);
// 插入新组图层
2025-10-13 15:08:37 +08:00
console.log("新增套索添加index", topLayerIndex);
this.layerManager.layers.value.splice(topLayerIndex, 0, this.groupLayer);
this.layerManager.activeLayerId.value = selectLayer.id; // 设置新组图层为活动图层
2025-06-29 23:29:47 +08:00
// 切换工具到选择模式
// 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);
}
2025-06-29 23:29:47 +08:00
this.canvas.discardActiveObject();
// this.canvas.setActiveObject(this.fabricImage);
await this.layerManager.updateLayersObjectsInteractivity(true);
2025-06-09 10:25:54 +08:00
console.log(`套索抠图完成新图层ID: ${this.newLayerId}`);
return {
newLayerId: this.newLayerId,
cutoutImageUrl: this.cutoutImageUrl,
2025-06-29 23:29:47 +08:00
guroupId: this.groupId,
groupName: this.groupName,
2025-06-09 10:25:54 +08:00
};
} catch (error) {
console.error("套索抠图过程中出错:", error);
// 如果已经创建了新图层,需要进行清理
if (this.newLayerId) {
try {
await this.layerManager.removeLayer(this.newLayerId);
} catch (cleanupError) {
console.warn("清理新图层失败:", cleanupError);
}
}
2025-06-29 23:29:47 +08:00
// 清理组图层(如果已创建)
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);
}
}
2025-06-09 10:25:54 +08:00
throw error;
}
}
async undo() {
try {
2025-06-29 23:29:47 +08:00
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
2025-06-29 23:29:47 +08:00
if (this.fabricImage) {
console.log(`↩️ 移除抠图图像: ${this.fabricImage.id}`);
// 从画布中移除抠图图像
this.canvas.remove(this.fabricImage);
}
// 2. 逆序撤销所有已执行的子命令
// RemoveLayerCommand的undo会自动恢复原图层所以不需要再调用_restoreOriginalLayer
2025-06-09 10:25:54 +08:00
for (let i = this.executedCommands.length - 1; i >= 0; i--) {
const command = this.executedCommands[i];
if (command && typeof command.undo === "function") {
2025-06-29 23:29:47 +08:00
try {
console.log(`↩️ 撤销子命令: ${command.constructor.name}`);
await command.undo();
console.log(`✅ 子命令撤销成功: ${command.constructor.name}`);
} catch (error) {
2025-07-21 01:17:25 +08:00
console.error(
`❌ 子命令撤销失败: ${command.constructor.name}`,
error
);
2025-06-29 23:29:47 +08:00
// 子命令撤销失败不中断整个撤销过程
}
2025-06-09 10:25:54 +08:00
}
}
// 注意不需要调用_restoreOriginalLayer因为RemoveLayerCommand的undo已经恢复了原图层
// 但是需要确保画布状态正确,所以手动触发一次渲染
if (this.canvas) {
this.canvas.renderAll();
}
2025-06-29 23:29:47 +08:00
// 3. 清理状态
2025-06-09 10:25:54 +08:00
this.executedCommands = [];
this.newLayerId = null;
this.cutoutImageUrl = null;
this.fabricImage = null;
2025-06-29 23:29:47 +08:00
this.groupLayer = null; // 清理组图层引用
// 注意不重置groupId因为重做时可能需要使用相同的ID
// 4. 更新画布和图层交互性
await this.layerManager.updateLayersObjectsInteractivity(true);
2025-06-09 10:25:54 +08:00
2025-06-29 23:29:47 +08:00
console.log(`✅ 套索抠图撤销完成`);
2025-06-09 10:25:54 +08:00
return true;
} catch (error) {
2025-06-29 23:29:47 +08:00
console.error("❌ 撤销套索抠图失败:", error);
2025-06-09 10:25:54 +08:00
return false;
}
}
2025-06-29 23:29:47 +08:00
/**
* 获取命令信息
* @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,
2025-06-29 23:29:47 +08:00
subCommands: this.executedCommands.map((cmd) => ({
name: cmd.constructor.name,
info: cmd.getInfo ? cmd.getInfo() : {},
})),
};
}
2025-06-09 10:25:54 +08:00
/**
2025-06-23 00:40:45 +08:00
* 获取图层的所有对象包括子图层从画布中查找真实对象
* @param {Object} layer 图层对象
* @returns {Array} 真实的fabric对象数组
* @private
*/
_getLayerObjects(layer) {
const objects = [];
const canvasObjects = this.canvas.getObjects();
// 递归获取图层及其子图层的所有对象
const collectLayerObjects = (currentLayer) => {
// 处理图层的fabricObjects
2025-07-21 01:17:25 +08:00
if (
currentLayer.fabricObjects &&
Array.isArray(currentLayer.fabricObjects)
) {
2025-06-23 00:40:45 +08:00
currentLayer.fabricObjects.forEach((fabricRef) => {
if (fabricRef && fabricRef.id) {
// 从画布中查找真实的对象
2025-07-21 01:17:25 +08:00
const realObject = canvasObjects.find(
(obj) => obj.id === fabricRef.id
);
2025-06-23 00:40:45 +08:00
if (realObject && realObject.visible) {
objects.push(realObject);
}
}
});
}
// 处理单个fabricObject背景图层等
if (currentLayer.fabricObject && currentLayer.fabricObject.id) {
2025-07-21 01:17:25 +08:00
const realObject = canvasObjects.find(
(obj) => obj.id === currentLayer.fabricObject.id
);
2025-06-23 00:40:45 +08:00
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
*/
2025-07-21 01:17:25 +08:00
async _performCutoutWithRasterized(
sourceObjects,
selectionObject,
selectionBounds
) {
2025-06-23 00:40:45 +08:00
try {
console.log("=== 开始使用createRasterizedImage执行抠图 ===");
console.log(`源对象数量: ${sourceObjects.length}`);
console.log(`选区边界:`, selectionBounds);
// 确定缩放因子,确保高质量输出
2025-06-23 00:40:45 +08:00
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, // 使用选区作为裁剪路径
2025-06-23 00:40:45 +08:00
trimWhitespace: true,
trimPadding: 2,
quality: 1.0,
format: "png",
scaleFactor: scaleFactor,
// isReturenDataURL: true, // 返回DataURL
preserveOriginalQuality: true, // 启用高质量模式
2025-06-29 23:29:47 +08:00
selectionManager: this.selectionManager, // 传递选区管理器,用于获取羽化值
2025-06-23 00:40:45 +08:00
});
if (!rasterizedDataURL) {
throw new Error("栅格化生成图像失败");
}
console.log(`✅ 高质量抠图完成,缩放因子: ${scaleFactor}x`);
2025-06-23 00:40:45 +08:00
return rasterizedDataURL;
} catch (error) {
console.error("使用createRasterizedImage执行抠图失败:", error);
// 如果createRasterizedImage失败回退到原始方法
console.log("⚠️ 回退到原始抠图方法...");
2025-07-21 01:17:25 +08:00
return await this._performCutout(
{ fabricObjects: sourceObjects },
selectionObject
);
2025-06-23 00:40:45 +08:00
}
}
/**
* 原始抠图方法作为备用方案
2025-06-09 10:25:54 +08:00
* @param {Object} sourceLayer 源图层
* @param {Object} selectionObject 选区对象
* @returns {String} 抠图结果的DataURL
* @private
*/
async _performCutout(sourceLayer, selectionObject) {
try {
2025-06-23 00:40:45 +08:00
console.log("=== 使用原始方法执行抠图 ===");
2025-06-09 10:25:54 +08:00
// 获取选区边界
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 {
// 收集源图层中的可见对象
2025-07-21 01:17:25 +08:00
const visibleObjects = sourceLayer.fabricObjects.filter(
(obj) => obj.visible
);
2025-06-09 10:25:54 +08:00
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,
};
2025-06-23 00:40:45 +08:00
// 设置高分辨率倍数
2025-06-09 10:25:54 +08:00
let highResolutionScale = 1;
if (this.highResolutionEnabled) {
const devicePixelRatio = window.devicePixelRatio || 1;
2025-07-21 01:17:25 +08:00
highResolutionScale = Math.max(
this.baseResolutionScale,
devicePixelRatio * 2
);
2025-06-09 10:25:54 +08:00
}
// 创建用于导出的高分辨率canvas
const exportCanvas = document.createElement("canvas");
const exportCtx = exportCanvas.getContext("2d", {
alpha: true,
willReadFrequently: false,
colorSpace: "srgb",
});
2025-07-21 01:17:25 +08:00
const actualWidth = Math.round(
renderBounds.width * highResolutionScale
);
const actualHeight = Math.round(
renderBounds.height * highResolutionScale
);
2025-06-09 10:25:54 +08:00
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) {
2025-06-23 00:40:45 +08:00
console.error("原始方法执行抠图失败:", error);
2025-06-09 10:25:54 +08:00
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;
}
2025-06-23 00:40:45 +08:00
// 使用选区的位置作为图像位置
let targetLeft = selectionBounds.left + selectionBounds.width / 2;
let targetTop = selectionBounds.top + selectionBounds.height / 2;
2025-06-09 10:25:54 +08:00
2025-06-23 00:40:45 +08:00
// 设置图像属性,保持选区的原始尺寸
2025-06-09 10:25:54 +08:00
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();
2025-06-23 00:40:45 +08:00
console.log(
`图像创建完成,位置: (${targetLeft}, ${targetTop}), 尺寸: ${img.width}x${img.height}`
);
2025-06-09 10:25:54 +08:00
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("对象克隆失败"));
}
2025-06-09 10:25:54 +08:00
});
} catch (error) {
reject(error);
}
});
}
2025-06-29 23:29:47 +08:00
/**
* 序列化选区对象
* @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);
}
// 尝试从选区管理器获取当前选区
2025-06-29 23:29:47 +08:00
const currentSelection = this.selectionManager.getSelectionObject();
if (currentSelection) {
console.log("从选区管理器获取到当前选区");
return currentSelection;
}
// 最后使用序列化数据恢复(备用方案)
2025-06-29 23:29:47 +08:00
if (!this.serializedSelectionObject) {
console.error("没有可用的选区对象数据");
2025-06-29 23:29:47 +08:00
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") {
2025-07-21 01:17:25 +08:00
fabric.Polygon.fromObject(
this.serializedSelectionObject,
(polygon) => {
if (polygon) {
console.log("多边形选区对象反序列化成功");
resolve(polygon);
} else {
reject(new Error("多边形选区对象反序列化失败"));
}
2025-06-29 23:29:47 +08:00
}
2025-07-21 01:17:25 +08:00
);
2025-06-29 23:29:47 +08:00
} 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") {
2025-07-21 01:17:25 +08:00
fabric.Ellipse.fromObject(
this.serializedSelectionObject,
(ellipse) => {
if (ellipse) {
console.log("椭圆选区对象反序列化成功");
resolve(ellipse);
} else {
reject(new Error("椭圆选区对象反序列化失败"));
}
2025-06-29 23:29:47 +08:00
}
2025-07-21 01:17:25 +08:00
);
2025-06-29 23:29:47 +08:00
} else {
// 通用对象反序列化
2025-07-21 01:17:25 +08:00
fabric.util.enlivenObjects(
[this.serializedSelectionObject],
(objects) => {
if (objects && objects.length > 0) {
console.log("通用选区对象反序列化成功");
resolve(objects[0]);
} else {
reject(new Error("通用选区对象反序列化失败"));
}
2025-06-29 23:29:47 +08:00
}
2025-07-21 01:17:25 +08:00
);
2025-06-29 23:29:47 +08:00
}
});
} 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) {
2025-07-21 01:17:25 +08:00
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) {
2025-07-21 01:17:25 +08:00
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();
}
2025-06-09 10:25:54 +08:00
}
/**
* 清除选区命令
*/
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 = {
// 选区对象的序列化数据
2025-07-21 01:17:25 +08:00
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;
}
2025-07-21 01:17:25 +08:00
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) {
2025-07-21 01:17:25 +08:00
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) {
2025-07-21 01:17:25 +08:00
console.warn(
`选区ID不匹配: 期望 ${originalId}, 实际 ${currentSelection.id}`
);
return false;
}
// 验证羽化值
const currentFeather = this.selectionManager.getFeatherAmount();
const originalFeather = this.originalSelectionState.featherAmount;
if (currentFeather !== originalFeather) {
2025-07-21 01:17:25 +08:00
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,
};
}
}