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

211 lines
7.6 KiB
JavaScript
Raw Normal View History

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 } = findLayerRecursively(this.layers.value, this.layerId);
this.layer = layer;
this.group = null;
this.originalfabricObjects = this._collectOriginalObjects(); // 记录所有的原始对象
// 计算所有对象的边界
this.originalInfo = this._getCurrentObjectsBoundingRect();
}
async execute() {
const layer = this.layer;
if (!layer.isBackground && !layer.isFixed && !layer.fabricObjects?.length) return false;
this.oldFill = layer.fill ?? null;
this.oldFillColor = layer.oldFillColor ?? null;
// 构建填充对象
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: 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,
// type: "fill",
});
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: this.fillColor,
layerId: this.layerId,
id: this.oldFill?.id || generateId("fill-"),
selectable: false,
evented: false,
originX: "center",
originY: "center",
// type: "fill",
});
}
}
// 判断fabricObjects是否是组对象
const firstObj = layer.fabricObjects?.[0];
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);
let insertIndex = this.canvas.getObjects()?.findIndex((obj) => obj.id === firstObj?.id) ?? 0;
insertIndex = insertIndex === -1 ? 0 : insertIndex;
this.group = new fabric.Group([this.newFill, ...layerObjects]);
this.group.set({
id: layerObjects[0]?.id || generateId("group-"),
layerId: layerObjects[0]?.layerId,
});
this.group.setCoords();
removeCanvasObjectByObject(this.canvas, layerObjects[0]);
insertObjectAtZIndex(this.canvas, this.group, insertIndex, false);
}
// layer.fabricObjects = [this.group?.toObject?.(["id", "layerId"]) || this.group];
this.canvas.renderAll();
layer.fill = null; // this.newFill.toObject(["id", "layerId"]);
layer.fillColor = this.fillColor;
this.canvasManager.thumbnailManager?.generateLayerThumbnail(this.layer.id);
return true;
}
async undo() {
this.layer.fillColor = this.oldFillColor;
this.layer.fill = this.oldFill;
// 判断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) {
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;
}
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,
};
}
}