2025-07-14 23:42:28 +08:00
|
|
|
|
import { Command } from "./Command";
|
|
|
|
|
|
import { findLayerRecursively } from "../utils/layerHelper";
|
|
|
|
|
|
import { fabric } from "fabric-with-all";
|
|
|
|
|
|
import {
|
|
|
|
|
|
findObjectById,
|
|
|
|
|
|
generateId,
|
|
|
|
|
|
insertObjectAtZIndex,
|
|
|
|
|
|
removeCanvasObjectByObject,
|
|
|
|
|
|
} from "../utils/helper";
|
2025-07-16 11:35:52 +08:00
|
|
|
|
import { restoreFabricObject } from "../utils/objectHelper";
|
2025-07-14 23:42:28 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 填充组图层背景命令
|
|
|
|
|
|
*/
|
2025-07-16 11:35:52 +08:00
|
|
|
|
export class FillGroupLayerBackgroundCommand extends Command {
|
2025-07-14 23:42:28 +08:00
|
|
|
|
constructor(options) {
|
|
|
|
|
|
super({ name: "填充组图层背景", saveState: true });
|
|
|
|
|
|
this.canvas = options.canvas;
|
|
|
|
|
|
this.layers = options.layers;
|
|
|
|
|
|
this.canvasManager = options.canvasManager;
|
|
|
|
|
|
this.layerId = options.layerId;
|
|
|
|
|
|
this.fillColor = options.fillColor;
|
|
|
|
|
|
this.oldFill = null;
|
|
|
|
|
|
this.oldFillColor = null;
|
|
|
|
|
|
this.newFill = null;
|
|
|
|
|
|
|
2025-07-20 19:50:22 +08:00
|
|
|
|
const { layer, parent } = findLayerRecursively(
|
|
|
|
|
|
this.layers.value,
|
|
|
|
|
|
this.layerId
|
|
|
|
|
|
);
|
2025-07-14 23:42:28 +08:00
|
|
|
|
|
|
|
|
|
|
this.layer = layer;
|
2025-07-17 13:46:13 +08:00
|
|
|
|
this.parent = parent;
|
2025-07-14 23:42:28 +08:00
|
|
|
|
|
|
|
|
|
|
this.group = null;
|
|
|
|
|
|
|
|
|
|
|
|
this.originalfabricObjects = this._collectOriginalObjects(); // 记录所有的原始对象
|
2025-07-16 11:35:52 +08:00
|
|
|
|
|
|
|
|
|
|
// 计算所有对象的边界
|
|
|
|
|
|
this.originalInfo = this._getCurrentObjectsBoundingRect();
|
2025-07-20 19:50:22 +08:00
|
|
|
|
|
|
|
|
|
|
this.firstObj = null; // 用于存储组图层的原始对象
|
2025-07-14 23:42:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async execute() {
|
|
|
|
|
|
const layer = this.layer;
|
2025-07-20 19:50:22 +08:00
|
|
|
|
if (!layer) return false;
|
2025-07-14 23:42:28 +08:00
|
|
|
|
|
|
|
|
|
|
this.oldFill = layer.fill ?? null;
|
|
|
|
|
|
this.oldFillColor = layer.oldFillColor ?? null;
|
|
|
|
|
|
|
2025-07-16 11:35:52 +08:00
|
|
|
|
// 构建填充对象
|
|
|
|
|
|
let clippingMaskFabricObject = null;
|
|
|
|
|
|
if (layer.clippingMask) {
|
2025-07-20 19:50:22 +08:00
|
|
|
|
clippingMaskFabricObject = await restoreFabricObject(
|
|
|
|
|
|
layer.clippingMask,
|
|
|
|
|
|
this.canvas
|
|
|
|
|
|
);
|
2025-07-16 11:35:52 +08:00
|
|
|
|
clippingMaskFabricObject.clipPath = null;
|
|
|
|
|
|
clippingMaskFabricObject.set({ absolutePositioned: true });
|
|
|
|
|
|
this.newFill = new fabric.Rect({
|
|
|
|
|
|
width: clippingMaskFabricObject.width,
|
|
|
|
|
|
height: clippingMaskFabricObject.height,
|
|
|
|
|
|
left: clippingMaskFabricObject.left || 0,
|
|
|
|
|
|
top: clippingMaskFabricObject.top || 0,
|
|
|
|
|
|
fill: this.fillColor,
|
|
|
|
|
|
layerId: this.layerId,
|
|
|
|
|
|
id: this.oldFill?.id || generateId("fill-"),
|
|
|
|
|
|
selectable: false,
|
|
|
|
|
|
evented: false,
|
|
|
|
|
|
originX: clippingMaskFabricObject.originX || "center",
|
|
|
|
|
|
originY: clippingMaskFabricObject.originY || "center",
|
|
|
|
|
|
scaleX: clippingMaskFabricObject.scaleX || 1,
|
|
|
|
|
|
scaleY: clippingMaskFabricObject.scaleY || 1,
|
|
|
|
|
|
});
|
2025-07-17 13:46:13 +08:00
|
|
|
|
// this.newFill.clipPath = clippingMaskFabricObject;
|
2025-07-16 11:35:52 +08:00
|
|
|
|
// this.newFill.dirty = true;
|
|
|
|
|
|
// this.newFill.setCoords();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
const originalInfo = this.originalInfo;
|
|
|
|
|
|
if (originalInfo) {
|
|
|
|
|
|
this.newFill = new fabric.Rect({
|
|
|
|
|
|
width: originalInfo.width,
|
|
|
|
|
|
height: originalInfo.height,
|
|
|
|
|
|
left: originalInfo.left + originalInfo.width / 2 || 0,
|
|
|
|
|
|
top: originalInfo.top + originalInfo.height / 2 || 0,
|
|
|
|
|
|
fill: this.fillColor,
|
|
|
|
|
|
layerId: this.layerId,
|
|
|
|
|
|
id: this.oldFill?.id || generateId("fill-"),
|
|
|
|
|
|
selectable: false,
|
|
|
|
|
|
evented: false,
|
|
|
|
|
|
originX: "center",
|
|
|
|
|
|
originY: "center",
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-07-14 23:42:28 +08:00
|
|
|
|
|
2025-07-16 11:35:52 +08:00
|
|
|
|
// 判断fabricObjects是否是组对象
|
2025-07-20 19:50:22 +08:00
|
|
|
|
const firstObj = layer.fabricObjects?.[0] || null;
|
|
|
|
|
|
// 如果没有找到第一个对象,则直接添加到当前画布
|
|
|
|
|
|
if (!firstObj) {
|
|
|
|
|
|
if (this?.parent?.clippingMask) {
|
|
|
|
|
|
clippingMaskFabricObject = await restoreFabricObject(
|
|
|
|
|
|
this.parent.clippingMask,
|
|
|
|
|
|
this.canvas
|
|
|
|
|
|
);
|
|
|
|
|
|
clippingMaskFabricObject.clipPath = null;
|
|
|
|
|
|
clippingMaskFabricObject.set({ absolutePositioned: true });
|
|
|
|
|
|
this.newFill = new fabric.Rect({
|
|
|
|
|
|
width: clippingMaskFabricObject.width,
|
|
|
|
|
|
height: clippingMaskFabricObject.height,
|
|
|
|
|
|
left: clippingMaskFabricObject.left || 0,
|
|
|
|
|
|
top: clippingMaskFabricObject.top || 0,
|
|
|
|
|
|
fill: this.fillColor,
|
|
|
|
|
|
layerId: this.layerId,
|
|
|
|
|
|
id: this.oldFill?.id || generateId("fill-"),
|
|
|
|
|
|
selectable: false,
|
|
|
|
|
|
evented: false,
|
|
|
|
|
|
originX: clippingMaskFabricObject.originX || "center",
|
|
|
|
|
|
originY: clippingMaskFabricObject.originY || "center",
|
|
|
|
|
|
scaleX: clippingMaskFabricObject.scaleX || 1,
|
|
|
|
|
|
scaleY: clippingMaskFabricObject.scaleY || 1,
|
|
|
|
|
|
});
|
2025-07-21 09:37:38 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
this.newFill = new fabric.Rect({
|
|
|
|
|
|
width: this.canvasManager?.canvasWidth?.value || this.canvas.width,
|
|
|
|
|
|
height: this.canvasManager?.canvasHeight?.value || this.canvas.height,
|
|
|
|
|
|
left: this.canvas.width / 2 || 0,
|
|
|
|
|
|
top: this.canvas.height / 2 || 0,
|
|
|
|
|
|
fill: this.fillColor,
|
|
|
|
|
|
layerId: this.layerId,
|
|
|
|
|
|
id: this.oldFill?.id || generateId("fill-"),
|
|
|
|
|
|
selectable: false,
|
|
|
|
|
|
evented: false,
|
|
|
|
|
|
originX: "center",
|
|
|
|
|
|
originY: "center",
|
|
|
|
|
|
});
|
2025-07-20 19:50:22 +08:00
|
|
|
|
}
|
2025-07-21 09:37:38 +08:00
|
|
|
|
this.newFill.clipPath = clippingMaskFabricObject; // 设置填充的遮罩
|
|
|
|
|
|
this.newFill.dirty = true; // 标记为脏,以便重新
|
|
|
|
|
|
this.canvas.add(this.newFill);
|
|
|
|
|
|
this.firstObj = this.newFill;
|
|
|
|
|
|
layer.fabricObjects = [
|
|
|
|
|
|
this.newFill.toObject(["id", "layerId"]) || this.newFill,
|
|
|
|
|
|
];
|
|
|
|
|
|
layer.fill = null; // this.newFill.toObject(["id", "layerId"]);
|
|
|
|
|
|
layer.fillColor = this.fillColor;
|
|
|
|
|
|
// 取消激活对象
|
|
|
|
|
|
|
|
|
|
|
|
this.canvas.discardActiveObject(); // 取消当前活动对象
|
|
|
|
|
|
// 重新排序
|
|
|
|
|
|
await this.layerManager?.sortLayersWithTool?.();
|
|
|
|
|
|
// 更新画布上对象的可选择状态
|
|
|
|
|
|
await this.layerManager?.updateLayersObjectsInteractivity?.();
|
|
|
|
|
|
this.canvas.renderAll();
|
2025-07-20 19:50:22 +08:00
|
|
|
|
|
2025-07-21 09:37:38 +08:00
|
|
|
|
this.canvasManager.thumbnailManager?.generateLayerThumbnail(
|
|
|
|
|
|
this.layer.id
|
|
|
|
|
|
);
|
2025-07-20 19:50:22 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2025-07-16 11:35:52 +08:00
|
|
|
|
const canvasObj = findObjectById(this.canvas, firstObj?.id)?.object;
|
2025-07-20 19:50:22 +08:00
|
|
|
|
if (
|
|
|
|
|
|
(canvasObj && canvasObj.type === "group") ||
|
|
|
|
|
|
canvasObj._objects?.length > 0
|
|
|
|
|
|
) {
|
2025-07-16 11:35:52 +08:00
|
|
|
|
this.newFill.set({
|
|
|
|
|
|
left: 0,
|
|
|
|
|
|
top: 0,
|
|
|
|
|
|
});
|
|
|
|
|
|
// 将新填充对象添加到组的最前面
|
|
|
|
|
|
// canvasObj._objects.unshift(this.newFill);
|
|
|
|
|
|
canvasObj.insertAt(this.newFill, 0, false);
|
|
|
|
|
|
// canvasObj.addWithUpdate();
|
|
|
|
|
|
canvasObj.setCoords();
|
|
|
|
|
|
canvasObj.setObjectsCoords();
|
2025-07-17 13:46:13 +08:00
|
|
|
|
canvasObj.dirty = true; // 标记为脏对象
|
2025-07-16 11:35:52 +08:00
|
|
|
|
// this.canvas.renderAll();
|
|
|
|
|
|
// this.group = canvasObj;
|
|
|
|
|
|
} else if (layer.fabricObjects && layer.fabricObjects.length > 0) {
|
|
|
|
|
|
// 普通对象,组成新组
|
2025-07-17 13:46:13 +08:00
|
|
|
|
const layerObjects =
|
2025-07-20 19:50:22 +08:00
|
|
|
|
this.canvas
|
|
|
|
|
|
.getObjects()
|
|
|
|
|
|
.filter((obj) => obj.layerId === this.layerId) || [];
|
2025-07-17 13:46:13 +08:00
|
|
|
|
// layerObjects?.forEach((obj) => {
|
|
|
|
|
|
// obj.clipPath = null;
|
|
|
|
|
|
// obj.dirty = true;
|
|
|
|
|
|
// obj.setCoords();
|
|
|
|
|
|
// });
|
2025-07-20 19:50:22 +08:00
|
|
|
|
let insertIndex =
|
|
|
|
|
|
this.canvas.getObjects()?.findIndex((obj) => obj.id === firstObj?.id) ??
|
|
|
|
|
|
0;
|
2025-07-16 11:35:52 +08:00
|
|
|
|
insertIndex = insertIndex === -1 ? 0 : insertIndex;
|
2025-07-17 13:46:13 +08:00
|
|
|
|
layerObjects.forEach((obj) => {
|
|
|
|
|
|
obj.clipPath = null;
|
|
|
|
|
|
});
|
2025-07-14 23:42:28 +08:00
|
|
|
|
this.group = new fabric.Group([this.newFill, ...layerObjects]);
|
|
|
|
|
|
this.group.set({
|
|
|
|
|
|
id: layerObjects[0]?.id || generateId("group-"),
|
2025-07-17 13:46:13 +08:00
|
|
|
|
layerId: this.layer?.id,
|
2025-07-14 23:42:28 +08:00
|
|
|
|
});
|
2025-07-17 13:46:13 +08:00
|
|
|
|
// this.group.setCoords();
|
|
|
|
|
|
// this.group.setObjectsCoords();
|
|
|
|
|
|
// this.group.dirty = true; // 标记为脏对象
|
|
|
|
|
|
if (this.parent?.clippingMask) {
|
2025-07-20 19:50:22 +08:00
|
|
|
|
const clipPath = await restoreFabricObject(
|
|
|
|
|
|
this.parent?.clippingMask,
|
|
|
|
|
|
this.canvas
|
|
|
|
|
|
);
|
2025-07-17 13:46:13 +08:00
|
|
|
|
clipPath.clipPath = null;
|
|
|
|
|
|
clipPath.set({ absolutePositioned: true });
|
|
|
|
|
|
this.group.clipPath = clipPath;
|
|
|
|
|
|
}
|
2025-07-20 19:50:22 +08:00
|
|
|
|
layer.fabricObjects = [
|
|
|
|
|
|
this.group.toObject(["id", "layerId"]) || this.group,
|
|
|
|
|
|
];
|
2025-07-17 13:46:13 +08:00
|
|
|
|
// removeCanvasObjectByObject(this.canvas, layerObjects?.[0]);
|
|
|
|
|
|
insertObjectAtZIndex(this.canvas, this.group, insertIndex, false, true);
|
2025-07-14 23:42:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-17 13:46:13 +08:00
|
|
|
|
// this.group?.addWithUpdate?.();
|
2025-07-16 11:35:52 +08:00
|
|
|
|
// layer.fabricObjects = [this.group?.toObject?.(["id", "layerId"]) || this.group];
|
2025-07-17 13:46:13 +08:00
|
|
|
|
// this.canvas.renderAll();
|
2025-07-16 11:35:52 +08:00
|
|
|
|
layer.fill = null; // this.newFill.toObject(["id", "layerId"]);
|
2025-07-14 23:42:28 +08:00
|
|
|
|
layer.fillColor = this.fillColor;
|
2025-07-17 13:46:13 +08:00
|
|
|
|
// 取消激活对象
|
|
|
|
|
|
|
|
|
|
|
|
this.canvas.discardActiveObject(); // 取消当前活动对象
|
|
|
|
|
|
// 重新排序
|
|
|
|
|
|
await this.layerManager?.sortLayersWithTool?.();
|
|
|
|
|
|
// 更新画布上对象的可选择状态
|
|
|
|
|
|
await this.layerManager?.updateLayersObjectsInteractivity?.();
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
|
2025-07-14 23:42:28 +08:00
|
|
|
|
this.canvasManager.thumbnailManager?.generateLayerThumbnail(this.layer.id);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
async undo() {
|
|
|
|
|
|
this.layer.fillColor = this.oldFillColor;
|
|
|
|
|
|
this.layer.fill = this.oldFill;
|
|
|
|
|
|
|
2025-07-20 19:50:22 +08:00
|
|
|
|
if (!this.originalInfo && this.firstObj) {
|
|
|
|
|
|
this.canvas.discardActiveObject();
|
|
|
|
|
|
this.canvas.remove(this.firstObj);
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
this.canvasManager.thumbnailManager?.generateLayerThumbnail(
|
|
|
|
|
|
this.parent.id
|
|
|
|
|
|
);
|
|
|
|
|
|
this.canvasManager.thumbnailManager?.generateLayerThumbnail(
|
|
|
|
|
|
this.layer.id
|
|
|
|
|
|
);
|
|
|
|
|
|
this.layer.fabricObjects = [];
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-16 11:35:52 +08:00
|
|
|
|
// 判断fabricObjects是否是组对象
|
|
|
|
|
|
const firstObj = this.layer.fabricObjects?.[0];
|
2025-07-20 19:50:22 +08:00
|
|
|
|
const canvasObj =
|
|
|
|
|
|
this.group || findObjectById(this.canvas, firstObj?.id)?.object;
|
2025-07-16 11:35:52 +08:00
|
|
|
|
|
2025-07-20 19:50:22 +08:00
|
|
|
|
if (
|
|
|
|
|
|
(canvasObj && canvasObj.type === "group") ||
|
|
|
|
|
|
canvasObj?._objects?.length > 0
|
|
|
|
|
|
) {
|
2025-07-16 11:35:52 +08:00
|
|
|
|
// 移除新添加的填充对象
|
|
|
|
|
|
if (canvasObj._objects?.[0] === this.newFill) {
|
|
|
|
|
|
canvasObj._objects.shift();
|
|
|
|
|
|
canvasObj.addWithUpdate();
|
|
|
|
|
|
canvasObj.setCoords();
|
|
|
|
|
|
canvasObj.dirty = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
} else if (this.group) {
|
|
|
|
|
|
// 如果是新建的group,需要拆分还原
|
|
|
|
|
|
// 先移除新建的group
|
|
|
|
|
|
this.canvas.remove(this.group);
|
|
|
|
|
|
// 重新添加原始对象
|
|
|
|
|
|
this.originalfabricObjects.forEach((obj) => {
|
|
|
|
|
|
if (!this.canvas.getObjects().includes(obj)) {
|
|
|
|
|
|
this.canvas.add(obj);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
this.group = null;
|
|
|
|
|
|
}
|
2025-07-14 23:42:28 +08:00
|
|
|
|
|
2025-07-17 13:46:13 +08:00
|
|
|
|
this.canvas.discardActiveObject(); // 取消当前活动对象
|
|
|
|
|
|
// 重新排序
|
|
|
|
|
|
await this.layerManager?.sortLayersWithTool?.();
|
|
|
|
|
|
// 更新画布上对象的可选择状态
|
|
|
|
|
|
await this.layerManager?.updateLayersObjectsInteractivity?.();
|
|
|
|
|
|
|
2025-07-14 23:42:28 +08:00
|
|
|
|
this.canvasManager.thumbnailManager?.generateLayerThumbnail(this.layer.id);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_collectOriginalObjects() {
|
2025-07-17 13:46:13 +08:00
|
|
|
|
if (this.layer?.children && this.layer?.children.length > 0) {
|
2025-07-14 23:42:28 +08:00
|
|
|
|
// 如果是组图层,收集所有子图层的fabric对象
|
|
|
|
|
|
return this.layer.children
|
|
|
|
|
|
.flatMap((child) => child.fabricObjects || [])
|
|
|
|
|
|
.map((obj) => {
|
|
|
|
|
|
return findObjectById(this.canvas.value, obj.id)?.object || obj;
|
|
|
|
|
|
});
|
2025-07-20 19:50:22 +08:00
|
|
|
|
} else if (
|
|
|
|
|
|
this.layer?.fabricObjects &&
|
|
|
|
|
|
this.layer?.fabricObjects?.length > 0
|
|
|
|
|
|
) {
|
2025-07-14 23:42:28 +08:00
|
|
|
|
// 如果是普通图层,直接返回其fabric对象
|
|
|
|
|
|
return this.layer.fabricObjects.map((obj) => {
|
|
|
|
|
|
return findObjectById(this.canvas.value, obj.id)?.object || obj;
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 如果没有fabric对象,返回空数组
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-07-16 11:35:52 +08:00
|
|
|
|
// 获取当前所有对象的边界信息
|
|
|
|
|
|
_getCurrentObjectsBoundingRect() {
|
|
|
|
|
|
// this.originalfabricObjects = this._collectOriginalObjects();
|
|
|
|
|
|
let minLeft = Infinity,
|
|
|
|
|
|
minTop = Infinity,
|
|
|
|
|
|
maxRight = -Infinity,
|
|
|
|
|
|
maxBottom = -Infinity;
|
2025-07-20 19:50:22 +08:00
|
|
|
|
console.log(
|
|
|
|
|
|
"计算当前所有对象的边界信息:===>",
|
|
|
|
|
|
this.originalfabricObjects.length
|
|
|
|
|
|
);
|
2025-07-16 11:35:52 +08:00
|
|
|
|
this.originalfabricObjects?.forEach((obj) => {
|
|
|
|
|
|
const { object } = findObjectById(this.canvas, obj.id) || {};
|
|
|
|
|
|
if (object) {
|
2025-07-20 19:50:22 +08:00
|
|
|
|
const rect = object.getBoundingRect({
|
|
|
|
|
|
absolute: true,
|
|
|
|
|
|
includeStroke: false,
|
|
|
|
|
|
});
|
2025-07-16 11:35:52 +08:00
|
|
|
|
minLeft = Math.min(minLeft, rect.left);
|
|
|
|
|
|
minTop = Math.min(minTop, rect.top);
|
|
|
|
|
|
maxRight = Math.max(maxRight, rect.left + rect.width);
|
|
|
|
|
|
maxBottom = Math.max(maxBottom, rect.top + rect.height);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
if (minLeft === Infinity || minTop === Infinity) return null;
|
|
|
|
|
|
return {
|
|
|
|
|
|
left: minLeft,
|
|
|
|
|
|
top: minTop,
|
|
|
|
|
|
width: maxRight - minLeft,
|
|
|
|
|
|
height: maxBottom - minTop,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
2025-07-14 23:42:28 +08:00
|
|
|
|
}
|