Files
aida_front/src/component/Canvas/CanvasEditor/commands/FillGroupLayerBackgroundCommand.js
2025-10-28 14:06:25 +08:00

370 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Command } from "./Command";
import { findLayerRecursively } from "../utils/layerHelper";
import { fabric } from "fabric-with-all";
import {
findObjectById,
generateId,
insertObjectAtZIndex,
removeCanvasObjectByObject,
} from "../utils/helper";
import { restoreFabricObject } from "../utils/objectHelper";
/**
* 填充组图层背景命令
*/
export class FillGroupLayerBackgroundCommand extends Command {
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;
const { layer, parent } = findLayerRecursively(
this.layers.value,
this.layerId
);
this.layer = layer;
this.parent = parent;
this.group = null;
this.originalfabricObjects = this._collectOriginalObjects(); // 记录所有的原始对象
// 计算所有对象的边界
this.originalInfo = this._getCurrentObjectsBoundingRect();
this.firstObj = null; // 用于存储组图层的原始对象
}
async execute(isUndo = false) {
const { layer, parent } = findLayerRecursively(
this.layers.value,
this.layerId
);
this.layer = layer;
this.parent = parent;
console.log("==========",layer);
if (!layer) return false;
if(!isUndo){
this.oldFill = layer.fill ?? null;
this.oldFillColor = layer.fillColor ?? null;
if(this.oldFill){
// 移除旧的填充对象
removeCanvasObjectByObject(this.canvas, this.oldFill);
}
}
const fillColor = isUndo ? this.oldFillColor : this.fillColor;
// 构建填充对象
let clippingMaskFabricObject = null;
if (layer.clippingMask) {
clippingMaskFabricObject = await restoreFabricObject(
layer.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: 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,
});
// this.newFill.clipPath = clippingMaskFabricObject;
// 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: fillColor,
layerId: this.layerId,
id: this.oldFill?.id || generateId("fill-"),
selectable: false,
evented: false,
originX: "center",
originY: "center",
});
}
}
// 判断fabricObjects是否是组对象
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: 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,
});
} 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: fillColor,
layerId: this.layerId,
id: this.oldFill?.id || generateId("fill-"),
selectable: false,
evented: false,
originX: "center",
originY: "center",
});
}
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 = this.newFill; // this.newFill.toObject(["id", "layerId"]);
layer.fillColor = fillColor;
// 取消激活对象
this.canvas.discardActiveObject(); // 取消当前活动对象
// 重新排序
await this.layerManager?.sortLayersWithTool?.();
// 更新画布上对象的可选择状态
await this.layerManager?.updateLayersObjectsInteractivity?.();
this.canvas.renderAll();
this.canvasManager.thumbnailManager?.generateLayerThumbnail(
this.layer.id
);
return false;
}
const canvasObj = findObjectById(this.canvas, firstObj?.id)?.object;
if (
(canvasObj && canvasObj.type === "group") ||
canvasObj._objects?.length > 0
) {
this.newFill.set({
left: 0,
top: 0,
});
// 将新填充对象添加到组的最前面
// canvasObj._objects.unshift(this.newFill);
canvasObj.insertAt(this.newFill, 0, false);
// canvasObj.addWithUpdate();
canvasObj.setCoords();
canvasObj.setObjectsCoords();
canvasObj.dirty = true; // 标记为脏对象
// this.canvas.renderAll();
// this.group = canvasObj;
} else if (layer.fabricObjects && layer.fabricObjects.length > 0) {
// 普通对象,组成新组
const layerObjects =
this.canvas
.getObjects()
.filter((obj) => obj.layerId === this.layerId) || [];
// layerObjects?.forEach((obj) => {
// obj.clipPath = null;
// obj.dirty = true;
// obj.setCoords();
// });
let insertIndex =
this.canvas.getObjects()?.findIndex((obj) => obj.id === firstObj?.id) ??
0;
insertIndex = insertIndex === -1 ? 0 : insertIndex;
layerObjects.forEach((obj) => {
obj.clipPath = null;
});
this.group = new fabric.Group([this.newFill, ...layerObjects]);
this.group.set({
id: layerObjects[0]?.id || generateId("group-"),
layerId: this.layer?.id,
});
// this.group.setCoords();
// this.group.setObjectsCoords();
// this.group.dirty = true; // 标记为脏对象
if (this.parent?.clippingMask) {
const clipPath = await restoreFabricObject(
this.parent?.clippingMask,
this.canvas
);
clipPath.clipPath = null;
clipPath.set({ absolutePositioned: true });
this.group.clipPath = clipPath;
}
layer.fabricObjects = [
this.group.toObject(["id", "layerId"]) || this.group,
];
// removeCanvasObjectByObject(this.canvas, layerObjects?.[0]);
insertObjectAtZIndex(this.canvas, this.group, insertIndex, false, true);
}
// this.group?.addWithUpdate?.();
// layer.fabricObjects = [this.group?.toObject?.(["id", "layerId"]) || this.group];
// this.canvas.renderAll();
layer.fill = this.newFill; // this.newFill.toObject(["id", "layerId"]);
layer.fillColor = fillColor;
// 取消激活对象
this.canvas.discardActiveObject(); // 取消当前活动对象
// 重新排序
await this.layerManager?.sortLayersWithTool?.();
// 更新画布上对象的可选择状态
await this.layerManager?.updateLayersObjectsInteractivity?.();
this.canvas.renderAll();
this.canvasManager.thumbnailManager?.generateLayerThumbnail(this.layer.id);
return true;
}
async undo() {
if (!this.originalInfo && this.layer.fill) {
this.canvas.discardActiveObject();
this.canvas.remove(this.layer.fill);
this.canvas.renderAll();
this.parent && this.canvasManager.thumbnailManager?.generateLayerThumbnail(
this.parent.id
);
this.canvasManager.thumbnailManager?.generateLayerThumbnail(
this.layer.id
);
this.layer.fabricObjects = [];
return false;
}
// 判断fabricObjects是否是组对象
const firstObj = this.layer.fabricObjects?.[0];
const canvasObj =
this.group || findObjectById(this.canvas, firstObj?.id)?.object;
if (
(canvasObj && canvasObj.type === "group") ||
canvasObj?._objects?.length > 0
) {
// 移除新添加的填充对象
// if (canvasObj._objects?.[0] === this.newFill) {
if (/^fill-/.test(canvasObj._objects?.[0]?.id)) {
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;
}
if(this.oldFill){
this.layer.fill = this.oldFill;
this.layer.fillColor = this.oldFillColor;
return this.execute(true);
}else{
this.layer.fill = null;
this.layer.fillColor = null;
}
this.canvas.discardActiveObject(); // 取消当前活动对象
// 重新排序
await this.layerManager?.sortLayersWithTool?.();
// 更新画布上对象的可选择状态
await this.layerManager?.updateLayersObjectsInteractivity?.();
this.canvasManager.thumbnailManager?.generateLayerThumbnail(this.layer.id);
return true;
}
_collectOriginalObjects() {
if (this.layer?.children && this.layer?.children.length > 0) {
// 如果是组图层收集所有子图层的fabric对象
return this.layer.children
.flatMap((child) => child.fabricObjects || [])
.map((obj) => {
return findObjectById(this.canvas.value, obj.id)?.object || obj;
});
} else if (
this.layer?.fabricObjects &&
this.layer?.fabricObjects?.length > 0
) {
// 如果是普通图层直接返回其fabric对象
return this.layer.fabricObjects.map((obj) => {
return findObjectById(this.canvas.value, obj.id)?.object || obj;
});
} else {
// 如果没有fabric对象返回空数组
return [];
}
}
// 获取当前所有对象的边界信息
_getCurrentObjectsBoundingRect() {
// this.originalfabricObjects = this._collectOriginalObjects();
let minLeft = Infinity,
minTop = Infinity,
maxRight = -Infinity,
maxBottom = -Infinity;
console.log(
"计算当前所有对象的边界信息:===>",
this.originalfabricObjects.length
);
this.originalfabricObjects?.forEach((obj) => {
const { object } = findObjectById(this.canvas, obj.id) || {};
if (object) {
const rect = object.getBoundingRect({
absolute: true,
includeStroke: false,
});
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,
};
}
}