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