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,4 +1,8 @@
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 } from "./LayerCommands.js";
@@ -52,7 +56,9 @@ export class CutSelectionToNewLayerCommand 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);
@@ -195,6 +201,17 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
this.groupLayer.clippingMask = clippingMask.toObject(["id", "layerId"]); // 设置组图层的fabricObject为遮罩图像
this.groupLayer.children.push(selectLayer);
selectionObject.set({
id: generateId("selectionObject-"),
customType: "selectionObject",
});
this.groupLayer.selectObject = selectionObject.toObject([
"id",
"customType",
]);
// 插入新组图层
this.layerManager.layers.value.splice(topLayerIndex, 1, this.groupLayer);
@@ -288,7 +305,10 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
await command.undo();
console.log(`✅ 子命令撤销成功: ${command.constructor.name}`);
} catch (error) {
console.error(`❌ 子命令撤销失败: ${command.constructor.name}`, error);
console.error(
`❌ 子命令撤销失败: ${command.constructor.name}`,
error
);
// 子命令撤销失败不中断整个撤销过程
}
}
@@ -352,11 +372,16 @@ export class CutSelectionToNewLayerCommand 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);
}
@@ -366,7 +391,9 @@ export class CutSelectionToNewLayerCommand 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);
}
@@ -397,7 +424,11 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
* @returns {String} 抠图结果的DataURL
* @private
*/
async _performCutoutWithRasterized(sourceObjects, selectionObject, selectionBounds) {
async _performCutoutWithRasterized(
sourceObjects,
selectionObject,
selectionBounds
) {
try {
console.log("=== 开始使用createRasterizedImage执行抠图 ===");
console.log(`源对象数量: ${sourceObjects.length}`);
@@ -436,7 +467,10 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
// 如果createRasterizedImage失败回退到原始方法
console.log("⚠️ 回退到原始抠图方法...");
return await this._performCutout({ fabricObjects: sourceObjects }, selectionObject);
return await this._performCutout(
{ fabricObjects: sourceObjects },
selectionObject
);
}
}
@@ -467,7 +501,9 @@ export class CutSelectionToNewLayerCommand 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("源图层没有可见对象");
@@ -520,7 +556,10 @@ export class CutSelectionToNewLayerCommand 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
@@ -531,8 +570,12 @@ export class CutSelectionToNewLayerCommand 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;
@@ -741,14 +784,17 @@ export class CutSelectionToNewLayerCommand 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) {
@@ -759,24 +805,30 @@ export class CutSelectionToNewLayerCommand 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) {

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;
}

View File

@@ -1,7 +1,15 @@
import { createLayer, findInChildLayers, LayerType, OperationType } from "../utils/layerHelper.js";
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 {
CreateImageLayerCommand,
RemoveLayerCommand,
} from "./LayerCommands.js";
import { fabric } from "fabric-with-all";
import { generateId } from "../utils/helper.js";
import { ToolCommand } from "./ToolCommands.js";
@@ -61,7 +69,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);
@@ -97,7 +107,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(
@@ -245,7 +262,8 @@ export class LassoCutoutCommand extends CompositeCommand {
// canvas: this.canvas,
// layers: this.layerManager.layers,
// layerId: this.originalLayer.id,
// activeLayerId: this.layerManager.activeLayerId,l
// activeLayerId: this.layerManager.activeLayerId,
// layerManager: this.layerManager,
// });
// // 执行删除原图层命令
@@ -254,6 +272,16 @@ export class LassoCutoutCommand extends CompositeCommand {
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);
// 插入新组图层
this.layerManager.layers.value.splice(topLayerIndex, 0, this.groupLayer);
@@ -368,7 +396,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
);
// 子命令撤销失败不中断整个撤销过程
}
}
@@ -445,11 +476,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);
}
@@ -459,7 +495,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);
}
@@ -490,7 +528,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}`);
@@ -529,7 +571,10 @@ export class LassoCutoutCommand extends CompositeCommand {
// 如果createRasterizedImage失败回退到原始方法
console.log("⚠️ 回退到原始抠图方法...");
return await this._performCutout({ fabricObjects: sourceObjects }, selectionObject);
return await this._performCutout(
{ fabricObjects: sourceObjects },
selectionObject
);
}
}
@@ -560,7 +605,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("源图层没有可见对象");
@@ -613,7 +660,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
@@ -624,8 +674,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;
@@ -834,14 +888,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) {
@@ -852,24 +909,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) {
@@ -893,7 +956,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);
@@ -901,7 +968,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) => {
@@ -1050,7 +1119,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(),
// 羽化值
@@ -1113,8 +1187,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;
@@ -1160,7 +1239,9 @@ export class ClearSelectionCommand extends Command {
// 恢复阴影(羽化效果)
if (selectionStyle.shadow) {
restoredObject.shadow = new fabric.Shadow(selectionStyle.shadow);
restoredObject.shadow = new fabric.Shadow(
selectionStyle.shadow
);
}
}
@@ -1245,7 +1326,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;
}
@@ -1253,7 +1336,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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -25,9 +25,13 @@ export class TransformCommand extends Command {
this.layers = options.layers || null;
this.lastSelectLayerId = options.lastSelectLayerId || null; // 最后选择的图层ID
const targetObject = findObjectById(this.canvas, this.objectId)?.object || null;
const targetObject =
findObjectById(this.canvas, this.objectId)?.object || null;
const { layer, parent } = findLayerRecursively(this.layers.value, targetObject?.layerId);
const { layer, parent } = findLayerRecursively(
this.layers.value,
targetObject?.layerId
);
this.layer = layer;
this.parent = parent;
@@ -107,7 +111,7 @@ export class TransformCommand extends Command {
if (
this.parent &&
this.parent?.clippingMask &&
this.parent?.children?.length === 1 &&
// this.parent?.children?.length === 1 &&
this.isSginleObject
) {
// 计算对象的变换位置
@@ -116,6 +120,20 @@ export class TransformCommand extends Command {
this.parent.clippingMask.left -= moveLeft;
this.parent.clippingMask.top -= moveTop;
if (this.parent.selectObject) {
// 如果有选区 则选区位置也要更新
this.parent.selectObject.left = this.parent.clippingMask.left;
this.parent.selectObject.top = this.parent.clippingMask.top;
const { object } = findObjectById(
this.canvas,
this.parent.selectObject?.id
);
object?.set({
left: this.parent.clippingMask.left,
top: this.parent.clippingMask.top,
});
object?.setCoords();
}
// 重新创建遮罩对象
const clippingMaskFabricObject = await restoreFabricObject(

View File

@@ -1,3 +1,4 @@
import { findObjectById } from "../utils/helper";
import { findLayerRecursively, isGroupLayer } from "../utils/layerHelper";
import { restoreFabricObject } from "../utils/objectHelper";
import { Command } from "./Command";
@@ -54,8 +55,12 @@ export class UpdateGroupMaskPositionCommand extends Command {
}) || this.target?.getBoundingRect?.(true, true);
this.originalSelectionPosition = {
left: this.isSginleObject ? this.target.left : this.activeSelection.left || 0,
top: this.isSginleObject ? this.target.top : this.activeSelection.top || 0,
left: this.isSginleObject
? this.target.left
: this.activeSelection.left || 0,
top: this.isSginleObject
? this.target.top
: this.activeSelection.top || 0,
};
console.log(
@@ -74,6 +79,21 @@ export class UpdateGroupMaskPositionCommand extends Command {
this.layer.clippingMask.left = newLeft;
this.layer.clippingMask.top = newTop;
if (this.layer.selectObject) {
// 如果有选区 则选区位置也要更新
this.layer.selectObject.left = this.layer.clippingMask.left;
this.layer.selectObject.top = this.layer.clippingMask.top;
const { object } = findObjectById(
this.canvas,
this.layer.selectObject?.id
);
object?.set({
left: this.layer.clippingMask.left,
top: this.layer.clippingMask.top,
});
object?.setCoords();
}
this.newMaskPosition = {
left: newLeft,
top: newTop,
@@ -112,6 +132,21 @@ export class UpdateGroupMaskPositionCommand extends Command {
layer.clippingMask.left = this.originalMaskPosition.left;
layer.clippingMask.top = this.originalMaskPosition.top;
if (this.layer.selectObject) {
// 如果有选区 则选区位置也要更新
this.layer.selectObject.left = this.originalMaskPosition.left;
this.layer.selectObject.top = this.originalMaskPosition.top;
const { object } = findObjectById(
this.canvas,
this.layer.selectObject?.id
);
object?.set({
left: this.originalMaskPosition.left,
top: this.originalMaskPosition.top,
});
object?.setCoords();
}
// 更新所有使用此遮罩的子图层对象
await this._updateChildObjectsClipPath(layer, true);
// await this.layerManager.updateLayersObjectsInteractivity();
@@ -147,6 +182,21 @@ export class UpdateGroupMaskPositionCommand extends Command {
layer.clippingMask.left = newLeft;
layer.clippingMask.top = newTop;
if (this.layer.selectObject) {
// 如果有选区 则选区位置也要更新
this.layer.selectObject.left = this.layer.clippingMask.left;
this.layer.selectObject.top = this.layer.clippingMask.top;
const { object } = findObjectById(
this.canvas,
this.layer.selectObject?.id
);
object?.set({
left: this.layer.clippingMask.left,
top: this.layer.clippingMask.top,
});
object?.setCoords();
}
// 更新所有使用此遮罩的子图层对象(不需要等待)
this._updateChildObjectsClipPath(layer);
@@ -173,7 +223,10 @@ export class UpdateGroupMaskPositionCommand extends Command {
try {
// 重新创建遮罩对象
const clippingMaskFabricObject = await restoreFabricObject(layer.clippingMask, this.canvas);
const clippingMaskFabricObject = await restoreFabricObject(
layer.clippingMask,
this.canvas
);
if (!clippingMaskFabricObject) {
console.warn("无法恢复遮罩对象");
@@ -192,7 +245,9 @@ export class UpdateGroupMaskPositionCommand extends Command {
layer.children.forEach((childLayer) => {
// 更新 fabricObjects 中的对象
childLayer.fabricObjects?.forEach((obj) => {
const fabricObject = this.canvas.getObjects().find((o) => o.id === obj.id);
const fabricObject = this.canvas
.getObjects()
.find((o) => o.id === obj.id);
if (fabricObject) {
fabricObject.clipPath = clippingMaskFabricObject;
fabricObject.dirty = true;
@@ -215,7 +270,9 @@ export class UpdateGroupMaskPositionCommand extends Command {
if (layer?.fill) {
// 更新组图层的填充颜色
const fabricObject = this.canvas.getObjects().find((o) => o.id === layer?.fill.id);
const fabricObject = this.canvas
.getObjects()
.find((o) => o.id === layer?.fill.id);
if (fabricObject) {
fabricObject.clipPath = clippingMaskFabricObject;
fabricObject.dirty = true;

View File

@@ -1,5 +1,7 @@
import { fabric } from "fabric-with-all";
import initAligningGuidelines, { initCenteringGuidelines } from "../utils/helperLine";
import initAligningGuidelines, {
initCenteringGuidelines,
} from "../utils/helperLine";
import { ThumbnailManager } from "./ThumbnailManager";
import { ExportManager } from "./ExportManager";
import {
@@ -14,7 +16,12 @@ import { CanvasEventManager } from "./events/CanvasEventManager";
import CanvasConfig from "../config/canvasConfig";
import { RedGreenModeManager } from "./RedGreenModeManager";
import { EraserStateManager } from "./EraserStateManager";
import { deepClone, generateId, optimizeCanvasRendering } from "../utils/helper";
import {
deepClone,
findObjectById,
generateId,
optimizeCanvasRendering,
} from "../utils/helper";
import { ChangeFixedImageCommand } from "../commands/ObjectLayerCommands";
import { isFunction } from "lodash-es";
import {
@@ -150,7 +157,10 @@ export class CanvasManager {
return false;
};
this.eraserStateManager = new EraserStateManager(this.canvas, this.layerManager);
this.eraserStateManager = new EraserStateManager(
this.canvas,
this.layerManager
);
// 监听擦除开始事件
this.canvas.on("erasing:start", () => {
@@ -171,12 +181,17 @@ export class CanvasManager {
}
// 更新交互性
command && (await this.layerManager?.updateLayersObjectsInteractivity?.());
command &&
(await this.layerManager?.updateLayersObjectsInteractivity?.());
this.thumbnailManager?.generateLayerThumbnail(this.layerManager?.activeLayerId?.value);
this.thumbnailManager?.generateLayerThumbnail(
this.layerManager?.activeLayerId?.value
);
// 固定图层 的擦除也需要重新生成缩略图 要判断 当前固定图层是否锁定
const fixedLayer = this.layers?.value?.find((layer) => layer.isFixed && !layer.locked);
const fixedLayer = this.layers?.value?.find(
(layer) => layer.isFixed && !layer.locked
);
// 如果有固定图层且未锁定,则生成缩略图
fixedLayer &&
this.isFixedErasable &&
@@ -366,7 +381,9 @@ export class CanvasManager {
// 设置固定图层的可擦除状态
layer.locked = flag;
// 更新画布对象的erasable属性
const fabricObject = this.canvas.getObjects().find((obj) => obj.id === layer.id);
const fabricObject = this.canvas
.getObjects()
.find((obj) => obj.id === layer.id);
if (fabricObject) {
fabricObject.erasable = flag;
fabricObject.set("erasable", flag);
@@ -478,7 +495,9 @@ export class CanvasManager {
const deltaY = backgroundObject.top - backgroundOldTop;
// 将相同的偏移量应用到所有其他对象上
const otherObjects = visibleObjects.filter((obj) => obj !== backgroundObject);
const otherObjects = visibleObjects.filter(
(obj) => obj !== backgroundObject
);
otherObjects.forEach((obj) => {
obj.set({
@@ -496,12 +515,29 @@ export class CanvasManager {
// 如果图层有遮罩,更新遮罩位置
layer.clippingMask.left += deltaX;
layer.clippingMask.top += deltaY;
if (layer.selectObject) {
// 如果有选区 则选区位置也要更新
layer.selectObject.left = layer.clippingMask.left;
layer.selectObject.top = layer.clippingMask.top;
const { object } = findObjectById(
this.canvas,
layer.selectObject?.id
);
object?.set({
left: layer.clippingMask.left,
top: layer.clippingMask.top,
});
object?.setCoords();
}
}
});
if (isMaskLayer) {
requestAnimationFrame(() => {
this.layerManager?.updateLayersObjectsInteractivity?.();
setTimeout(() => {
this.layerManager?.updateLayersObjectsInteractivity?.(false, {
isMoveing: true,
});
});
}
@@ -598,7 +634,8 @@ export class CanvasManager {
if (!backgroundLayerObject) return;
const bgWidth = backgroundLayerObject.width * backgroundLayerObject.scaleX;
const bgHeight = backgroundLayerObject.height * backgroundLayerObject.scaleY;
const bgHeight =
backgroundLayerObject.height * backgroundLayerObject.scaleY;
const left = backgroundLayerObject.left;
const top = backgroundLayerObject.top;
@@ -659,7 +696,9 @@ export class CanvasManager {
return obj.isBackground || obj.id === backgroundLayerId;
});
if (!backgroundLayerByBgLayer) {
console.warn("CanvasManager.js = >getBackgroundLayer 方法没有找到背景层");
console.warn(
"CanvasManager.js = >getBackgroundLayer 方法没有找到背景层"
);
}
return backgroundLayerByBgLayer;
@@ -669,7 +708,8 @@ export class CanvasManager {
* @param {Object} backgroundLayerObject 背景层对象
*/
updateMaskPosition(backgroundLayerObject) {
if (!backgroundLayerObject || !this.maskLayer || !this.canvas.clipPath) return;
if (!backgroundLayerObject || !this.maskLayer || !this.canvas.clipPath)
return;
const left = backgroundLayerObject.left;
const top = backgroundLayerObject.top;
@@ -734,7 +774,8 @@ export class CanvasManager {
...options,
});
command.undoable = options.undoable !== undefined ? options.undoable : false; // 默认不可撤销 undoable = true 为可撤销
command.undoable =
options.undoable !== undefined ? options.undoable : false; // 默认不可撤销 undoable = true 为可撤销
const result = (await command?.execute?.()) || {
success: false,
@@ -784,7 +825,9 @@ export class CanvasManager {
...options,
// 如果没有明确指定,则根据当前模式自动设置
restoreOpacityInRedGreen:
options.restoreOpacityInRedGreen !== undefined ? options.restoreOpacityInRedGreen : true, // 默认在红绿图模式下恢复透明度
options.restoreOpacityInRedGreen !== undefined
? options.restoreOpacityInRedGreen
: true, // 默认在红绿图模式下恢复透明度
};
// 如果在红绿图模式下且没有指定具体的图层,自动包含所有普通图层
@@ -798,7 +841,9 @@ export class CanvasManager {
// 获取所有非背景、非固定的普通图层ID
const normalLayerIds =
this.layers?.value
?.filter((layer) => !layer.isBackground && !layer.isFixed && layer.visible)
?.filter(
(layer) => !layer.isBackground && !layer.isFixed && layer.visible
)
?.map((layer) => layer.id) || [];
if (normalLayerIds.length > 0) {
@@ -911,7 +956,9 @@ export class CanvasManager {
this.canvas.discardActiveObject();
this.canvas.renderAll();
const simplifyLayersData = simplifyLayers(JSON.parse(JSON.stringify(this.layers.value)));
const simplifyLayersData = simplifyLayers(
JSON.parse(JSON.stringify(this.layers.value))
);
console.log("获取画布JSON数据...", simplifyLayersData);
return JSON.stringify({
canvas: this.canvas.toJSON([
@@ -927,6 +974,7 @@ export class CanvasManager {
"eraser",
"eraserable",
"erasable",
"customType",
]),
layers: simplifyLayersData, // 简化图层数据
// layers: JSON.stringify(JSON.parse(JSON.stringify(this.layers.value))), // 全数据
@@ -1033,7 +1081,8 @@ export class CanvasManager {
// 使用LayerSort工具重新排列画布对象如果可用
await this?.layerManager?.layerSort?.rearrangeObjects();
this.layerManager.activeLayerId.value = this.layers.value[0].children?.length
this.layerManager.activeLayerId.value = this.layers.value[0]
.children?.length
? this.layers.value[0].children[0].id
: this.layers.value[0]?.id || parsedJson?.activeLayerId || null;
@@ -1046,7 +1095,9 @@ export class CanvasManager {
await calllBack?.();
// 确保所有对象的交互性正确设置
await this.layerManager?.updateLayersObjectsInteractivity?.(false);
await this.layerManager?.updateLayersObjectsInteractivity?.(
false
);
console.log(this.layerManager.layers.value);
// 更新所有缩略图
@@ -1203,7 +1254,9 @@ export class CanvasManager {
if (!this.layers || !this.layers.value) return [];
// 查找所有非背景、非固定的普通图层
const normalLayers = this.layers.value.filter((layer) => !layer.isBackground && !layer.isFixed);
const normalLayers = this.layers.value.filter(
(layer) => !layer.isBackground && !layer.isFixed
);
// 收集所有普通图层中的对象
const objects = [];
@@ -1240,9 +1293,15 @@ export class CanvasManager {
// 比较尺寸允许5%的误差)
const sizeMatch =
Math.abs(obj.width * obj.scaleX - fixedLayerObject.width * fixedLayerObject.scaleX) <
Math.abs(
obj.width * obj.scaleX -
fixedLayerObject.width * fixedLayerObject.scaleX
) <
fixedLayerObject.width * fixedLayerObject.scaleX * 0.05 &&
Math.abs(obj.height * obj.scaleY - fixedLayerObject.height * fixedLayerObject.scaleY) <
Math.abs(
obj.height * obj.scaleY -
fixedLayerObject.height * fixedLayerObject.scaleY
) <
fixedLayerObject.height * fixedLayerObject.scaleY * 0.05;
// 比较位置(允许一定的偏差)

View File

@@ -64,6 +64,7 @@ import { isBoolean, template } from "lodash-es";
import {
findObjectById,
generateId,
insertObjectAtZIndex,
optimizeCanvasRendering,
} from "../utils/helper";
import { message } from "ant-design-vue";
@@ -189,19 +190,40 @@ export class LayerManager {
* 根据当前编辑模式和图层状态设置对象的交互属性
* @private
*/
async updateLayersObjectsInteractivity(isUseOptimize = true) {
async updateLayersObjectsInteractivity(
isUseOptimize = true,
{ isMoveing = false } = {}
) {
if (!this.canvas) return;
if (isUseOptimize) {
// 优化渲染 - 统一批处理 支持异步回调
await optimizeCanvasRendering(this.canvas, async () => {
// 应用图层交互规则
await this._applyInteractionRules();
await this._applyInteractionRules({ isMoveing });
});
} else {
// 直接应用图层交互规则
await this._applyInteractionRules();
await this._applyInteractionRules({ isMoveing });
}
// customType: "selectionObject",
// 清空没有关联的选区图层
const selectionObjects = this.canvas
.getObjects()
?.filter((obj) => obj.customType == "selectionObject");
selectionObjects.forEach((obj) => {
const isObjExtis = this.layers?.value.find(
(layer) => layer?.selectObject?.id == obj.id
);
if (!isObjExtis) {
// 不存在则移除对象
this?.canvas?.remove(obj);
}
});
selectionObjects && this.canvas.renderAll();
// // 性能优化使用requestAnimationFrame
// requestAnimationFrame(() => {
// // 暂停渲染以提高性能
@@ -304,7 +326,7 @@ export class LayerManager {
}
// 私有方法:应用交互规则
async _applyInteractionRules() {
async _applyInteractionRules({ isMoveing }) {
console.log("updateLayersObjectsInteractivity ===>", this.editorMode);
const objects = this.canvas.getObjects();
const editorMode = this.editorMode || CanvasConfig.defaultTool;
@@ -414,6 +436,68 @@ export class LayerManager {
// }
// }
}
// 设置画布选区内容
if (layer.selectObject && !isMoveing) {
// 如果有选区对象
let insetIndex = objects.findIndex(
(o) => o.id === layer.selectObject.id
);
let isOldSelectObject = insetIndex >= 0; // 是否存在旧的选区对象
const newSelectObject = await restoreFabricObject(
layer.selectObject,
this.canvas
);
if (insetIndex < 0) {
// 如果没有找到选区对象,则添加到画布 先查找子图层元素下标 如果没有则要向前查找有元素的下标 否则直接添加,因为选区要遮罩在选区图层的最上面
layer.children?.forEach((childLayer) => {
const childObj = this.canvas
.getObjects()
.find((o) => o.layerId === childLayer.id);
if (childObj) {
insetIndex = objects.indexOf(childObj);
}
});
}
// 向前查找,找不到则添加到最后
if (insetIndex < 0) {
// 如果没有找到选区对象,则添加到画布
const layerIndex = this.layers.value.findIndex(
(ll) => ll.id === layer.id
);
for (let i = layerIndex - 1; i >= 0; i--) {
const l = this.layers.value[i];
if (l.fabricObjects?.length > 0) {
insetIndex = objects.findIndex(
(o) => o.id === l.fabricObjects?.[0].id
);
if (insetIndex >= 0) break; // 找到第一个有对象的图层
}
}
insetIndex = insetIndex < 0 ? objects.length : insetIndex;
}
newSelectObject.set({
id: layer.selectObject.id,
customType: layer.selectObject.customType,
selectable: false,
evented: false,
erasable: false,
});
// 有则更新对象
// 没有则反序列创建新的选区对象
insertObjectAtZIndex(
this.canvas,
newSelectObject,
insetIndex,
false,
isOldSelectObject
);
}
});
}
@@ -871,6 +955,7 @@ export class LayerManager {
layers: this.layers,
layerId: layerId,
activeLayerId: this.activeLayerId,
layerManager: this,
});
// 执行命令

View File

@@ -15,13 +15,18 @@ export function buildLayerAssociations(layer, canvasObjects) {
// 处理单个fabricObject关联
if (layer.fabricObject) {
// 如果图层已经有关联的fabricObject确保它的layerId和layerName正确
layer.fabricObject = canvasObjects.find((obj) => obj.id === layer.fabricObject.id) || null;
layer.fabricObject =
canvasObjects.find((obj) => obj.id === layer.fabricObject.id) || null;
}
if (layer.clippingMask) {
// clippingMask 可能是一个fabricObject或组 也可能是一个简单对象
const clippingMaskObj = canvasObjects.find((obj) => obj.id === layer.clippingMask.id);
layer.clippingMask = clippingMaskObj ? clippingMaskObj?.toObject?.(["id"]) : layer.clippingMask;
const clippingMaskObj = canvasObjects.find(
(obj) => obj.id === layer.clippingMask.id
);
layer.clippingMask = clippingMaskObj
? clippingMaskObj?.toObject?.(["id"])
: layer.clippingMask;
}
// 处理多个fabricObjects关联
@@ -167,7 +172,10 @@ export function simplifyLayers(layers) {
opacity: layer.opacity,
isBackground: layer.isBackground || false,
isFixed: layer.isFixed || false,
clippingMask: layer.clippingMask?.toObject?.(["id", "layerId"]) || layer.clippingMask || null, // 可能是一个fabricObject或组 也可能是一个简单对象
clippingMask:
layer.clippingMask?.toObject?.(["id", "layerId"]) ||
layer.clippingMask ||
null, // 可能是一个fabricObject或组 也可能是一个简单对象
// ? {
// id: layer.clippingMask.id,
// type: layer.clippingMask.type,
@@ -192,9 +200,13 @@ export function simplifyLayers(layers) {
)
.filter((obj) => obj !== null)
: [],
children: layer.children && isArray(layer.children) ? simplifyLayers(layer.children) : [],
children:
layer.children && isArray(layer.children)
? simplifyLayers(layer.children)
: [],
fill: layer?.fill || null,
fillColor: layer.fillColor,
selectObject: layer.selectObject,
};
return simplifiedLayer;
@@ -231,7 +243,9 @@ export function restoreLayers(simplifiedLayers, canvasObjects) {
if (layer.clippingMask) {
// clippingMask 可能是一个fabricObject或组 也可能是一个简单对象
const clippingMaskObj = canvasObjects.find((obj) => obj.id === layer.clippingMask.id);
const clippingMaskObj = canvasObjects.find(
(obj) => obj.id === layer.clippingMask.id
);
restoredLayer.clippingMask = clippingMaskObj
? clippingMaskObj?.toObject?.(["id"])
: layer.clippingMask;
@@ -239,11 +253,17 @@ export function restoreLayers(simplifiedLayers, canvasObjects) {
// 恢复单个fabricObject关联
if (layer.fabricObject?.id) {
const fabricObj = canvasObjects.find((obj) => obj.id === layer.fabricObject.id);
const fabricObj = canvasObjects.find(
(obj) => obj.id === layer.fabricObject.id
);
if (fabricObj) {
fabricObj.layerId = layer.id;
fabricObj.layerName = layer.name;
restoredLayer.fabricObject = fabricObj.toObject(["id", "layerId", "type"]);
restoredLayer.fabricObject = fabricObj.toObject([
"id",
"layerId",
"type",
]);
} else {
restoredLayer.fabricObject = null;
}
@@ -253,7 +273,9 @@ export function restoreLayers(simplifiedLayers, canvasObjects) {
if (layer.fabricObjects && isArray(layer.fabricObjects)) {
restoredLayer.fabricObjects = layer.fabricObjects
.map((fabricRef) => {
const fabricObj = canvasObjects.find((obj) => obj.id === fabricRef.id);
const fabricObj = canvasObjects.find(
(obj) => obj.id === fabricRef.id
);
if (fabricObj) {
fabricObj.layerId = layer.id;
fabricObj.layerName = layer.name;