feat : 显示选区逻辑完成

This commit is contained in:
bighuixiang
2025-07-21 01:17:25 +08:00
parent ba04930966
commit c756d7377f
9 changed files with 983 additions and 308 deletions

View File

@@ -1,7 +1,14 @@
import { createLayer, findInChildLayers, LayerType } from "../utils/layerHelper.js";
import {
createLayer,
findInChildLayers,
LayerType,
} from "../utils/layerHelper.js";
import { createRasterizedImage } from "../utils/selectionToImage.js";
import { CompositeCommand, Command } from "./Command.js";
import { CreateImageLayerCommand, RemoveLayerCommand } from "./LayerCommands.js";
import {
CreateImageLayerCommand,
RemoveLayerCommand,
} from "./LayerCommands.js";
import { fabric } from "fabric-with-all";
import { generateId } from "../utils/helper.js";
@@ -58,7 +65,9 @@ export class LassoCutoutCommand extends CompositeCommand {
const selectionObject = this.selectionManager.getSelectionObject();
if (selectionObject) {
try {
this._clonedSelectionObject = await this._cloneObject(selectionObject);
this._clonedSelectionObject = await this._cloneObject(
selectionObject
);
console.log("套索抠图:选区对象已克隆保存");
} catch (error) {
console.error("套索抠图:克隆选区对象失败:", error);
@@ -94,7 +103,14 @@ export class LassoCutoutCommand extends CompositeCommand {
const sourceObjects = this._getLayerObjects(activeLayer);
this.originalCanvasObjects = sourceObjects; // 保存真实对象引用
this.originalFabricObjects = sourceObjects.map((obj) =>
obj.toObject(["id", "layerId", "layerName", "parentId", "type", "custom"])
obj.toObject([
"id",
"layerId",
"layerName",
"parentId",
"type",
"custom",
])
);
console.log(
@@ -196,6 +212,7 @@ export class LassoCutoutCommand extends CompositeCommand {
layers: this.layerManager.layers,
layerId: this.originalLayer.id,
activeLayerId: this.layerManager.activeLayerId,
layerManager: this.layerManager,
});
// 执行删除原图层命令
@@ -337,7 +354,10 @@ export class LassoCutoutCommand extends CompositeCommand {
await command.undo();
console.log(`✅ 子命令撤销成功: ${command.constructor.name}`);
} catch (error) {
console.error(`❌ 子命令撤销失败: ${command.constructor.name}`, error);
console.error(
`❌ 子命令撤销失败: ${command.constructor.name}`,
error
);
// 子命令撤销失败不中断整个撤销过程
}
}
@@ -414,11 +434,16 @@ export class LassoCutoutCommand extends CompositeCommand {
// 递归获取图层及其子图层的所有对象
const collectLayerObjects = (currentLayer) => {
// 处理图层的fabricObjects
if (currentLayer.fabricObjects && Array.isArray(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);
const realObject = canvasObjects.find(
(obj) => obj.id === fabricRef.id
);
if (realObject && realObject.visible) {
objects.push(realObject);
}
@@ -428,7 +453,9 @@ export class LassoCutoutCommand extends CompositeCommand {
// 处理单个fabricObject背景图层等
if (currentLayer.fabricObject && currentLayer.fabricObject.id) {
const realObject = canvasObjects.find((obj) => obj.id === currentLayer.fabricObject.id);
const realObject = canvasObjects.find(
(obj) => obj.id === currentLayer.fabricObject.id
);
if (realObject && realObject.visible) {
objects.push(realObject);
}
@@ -459,7 +486,11 @@ export class LassoCutoutCommand extends CompositeCommand {
* @returns {String} 抠图结果的DataURL
* @private
*/
async _performCutoutWithRasterized(sourceObjects, selectionObject, selectionBounds) {
async _performCutoutWithRasterized(
sourceObjects,
selectionObject,
selectionBounds
) {
try {
console.log("=== 开始使用createRasterizedImage执行抠图 ===");
console.log(`源对象数量: ${sourceObjects.length}`);
@@ -498,7 +529,10 @@ export class LassoCutoutCommand extends CompositeCommand {
// 如果createRasterizedImage失败回退到原始方法
console.log("⚠️ 回退到原始抠图方法...");
return await this._performCutout({ fabricObjects: sourceObjects }, selectionObject);
return await this._performCutout(
{ fabricObjects: sourceObjects },
selectionObject
);
}
}
@@ -529,7 +563,9 @@ export class LassoCutoutCommand extends CompositeCommand {
try {
// 收集源图层中的可见对象
const visibleObjects = sourceLayer.fabricObjects.filter((obj) => obj.visible);
const visibleObjects = sourceLayer.fabricObjects.filter(
(obj) => obj.visible
);
if (visibleObjects.length === 0) {
throw new Error("源图层没有可见对象");
@@ -582,7 +618,10 @@ export class LassoCutoutCommand extends CompositeCommand {
let highResolutionScale = 1;
if (this.highResolutionEnabled) {
const devicePixelRatio = window.devicePixelRatio || 1;
highResolutionScale = Math.max(this.baseResolutionScale, devicePixelRatio * 2);
highResolutionScale = Math.max(
this.baseResolutionScale,
devicePixelRatio * 2
);
}
// 创建用于导出的高分辨率canvas
@@ -593,8 +632,12 @@ export class LassoCutoutCommand extends CompositeCommand {
colorSpace: "srgb",
});
const actualWidth = Math.round(renderBounds.width * highResolutionScale);
const actualHeight = Math.round(renderBounds.height * highResolutionScale);
const actualWidth = Math.round(
renderBounds.width * highResolutionScale
);
const actualHeight = Math.round(
renderBounds.height * highResolutionScale
);
exportCanvas.width = actualWidth;
exportCanvas.height = actualHeight;
@@ -803,14 +846,17 @@ export class LassoCutoutCommand extends CompositeCommand {
}
});
} else if (objectType === "polygon") {
fabric.Polygon.fromObject(this.serializedSelectionObject, (polygon) => {
if (polygon) {
console.log("多边形选区对象反序列化成功");
resolve(polygon);
} else {
reject(new Error("多边形选区对象反序列化失败"));
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) {
@@ -821,24 +867,30 @@ export class LassoCutoutCommand extends CompositeCommand {
}
});
} else if (objectType === "ellipse" || objectType === "circle") {
fabric.Ellipse.fromObject(this.serializedSelectionObject, (ellipse) => {
if (ellipse) {
console.log("椭圆选区对象反序列化成功");
resolve(ellipse);
} else {
reject(new Error("椭圆选区对象反序列化失败"));
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("通用选区对象反序列化失败"));
fabric.util.enlivenObjects(
[this.serializedSelectionObject],
(objects) => {
if (objects && objects.length > 0) {
console.log("通用选区对象反序列化成功");
resolve(objects[0]);
} else {
reject(new Error("通用选区对象反序列化失败"));
}
}
});
);
}
});
} catch (error) {
@@ -862,7 +914,11 @@ export class LassoCutoutCommand extends CompositeCommand {
// 1. 恢复图层到原位置
if (this.originalLayerIndex !== -1) {
this.layerManager.layers.value.splice(this.originalLayerIndex, 0, this.originalLayer);
this.layerManager.layers.value.splice(
this.originalLayerIndex,
0,
this.originalLayer
);
} else {
// 如果没有保存索引,添加到末尾
this.layerManager.layers.value.push(this.originalLayer);
@@ -870,7 +926,9 @@ export class LassoCutoutCommand extends CompositeCommand {
// 2. 恢复fabric对象到画布
if (this.originalFabricObjects.length > 0) {
console.log(`↩️ 恢复 ${this.originalFabricObjects.length} 个fabric对象`);
console.log(
`↩️ 恢复 ${this.originalFabricObjects.length} 个fabric对象`
);
// 使用fabric.util.enlivenObjects批量反序列化对象
await new Promise((resolve, reject) => {
@@ -1019,7 +1077,12 @@ export class ClearSelectionCommand extends Command {
// 序列化选区对象和相关状态
this.originalSelectionState = {
// 选区对象的序列化数据
selectionObjectData: selectionObject.toObject(["id", "layerId", "layerName", "parentId"]),
selectionObjectData: selectionObject.toObject([
"id",
"layerId",
"layerName",
"parentId",
]),
// 选区路径数据
selectionPath: this.selectionManager.getSelectionPath(),
// 羽化值
@@ -1082,8 +1145,13 @@ export class ClearSelectionCommand extends Command {
return;
}
const { selectionObjectData, featherAmount, selectionStyle, position, managerState } =
this.originalSelectionState;
const {
selectionObjectData,
featherAmount,
selectionStyle,
position,
managerState,
} = this.originalSelectionState;
// 根据选区对象类型进行反序列化
const objectType = selectionObjectData.type;
@@ -1129,7 +1197,9 @@ export class ClearSelectionCommand extends Command {
// 恢复阴影(羽化效果)
if (selectionStyle.shadow) {
restoredObject.shadow = new fabric.Shadow(selectionStyle.shadow);
restoredObject.shadow = new fabric.Shadow(
selectionStyle.shadow
);
}
}
@@ -1214,7 +1284,9 @@ export class ClearSelectionCommand extends Command {
// 验证基本属性
const originalId = this.originalSelectionState.selectionId;
if (currentSelection.id !== originalId) {
console.warn(`选区ID不匹配: 期望 ${originalId}, 实际 ${currentSelection.id}`);
console.warn(
`选区ID不匹配: 期望 ${originalId}, 实际 ${currentSelection.id}`
);
return false;
}
@@ -1222,7 +1294,9 @@ export class ClearSelectionCommand extends Command {
const currentFeather = this.selectionManager.getFeatherAmount();
const originalFeather = this.originalSelectionState.featherAmount;
if (currentFeather !== originalFeather) {
console.warn(`羽化值不匹配: 期望 ${originalFeather}, 实际 ${currentFeather}`);
console.warn(
`羽化值不匹配: 期望 ${originalFeather}, 实际 ${currentFeather}`
);
return false;
}