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 FillLayerBackgroundCommand 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.isRetimeUpdate = options.isRetimeUpdate; // 是否实时更新 this.oldFill = null; this.oldFillColor = null; this.newFill = null; const { layer } = findLayerRecursively(this.layers.value, this.layerId); this.layer = layer; this.group = null; this.isGroupLayer = layer.isGroup || !!layer.children?.length || false; // 是否为组图层 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; // 更新fabric对象填充 // 1.如果图层有遮罩则用遮罩 2.如果没有遮罩则汇总当前fabricObjects下的所有对象的边界 let clippingMaskFabricObject = null; if (layer.clippingMask) { // 反序列化 clippingMask clippingMaskFabricObject = await restoreFabricObject(layer.clippingMask, this.canvas); clippingMaskFabricObject.clipPath = null; clippingMaskFabricObject.set({ // 设置绝对定位 // ...getOriginObjectInfo(layer.clippingMask), // 恢复原定位 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是否是组,是组则添加填充到最前面,否则创建组 if (layer.fabricObjects && layer.fabricObjects.length > 0) { let insertIndex = this.canvas.getObjects()?.findIndex((obj) => obj.id === layer.fabricObjects?.[0]?.id) || 0; if (this.oldFill) { // 如果有旧填充,先获取旧的索引再移除旧填充 insertIndex = this.canvas.getObjects()?.findIndex((obj) => obj.id === this.oldFill?.id) || 0; removeCanvasObjectByObject(this.canvas, this.oldFill); } insertIndex = insertIndex == -1 ? 0 : insertIndex; // layer.fabricObjects = [this.newFill?.toObject?.(["id", "layerId"]) || this.newFill]; insertObjectAtZIndex(this.canvas, this.newFill, insertIndex, false); // 插入到画布的指定位置 // 将新填充对象添加到画布 } else if (layer.children && layer.children.length > 0) { // 如果是组图层,直接添加到组中 let insertIndex = this.canvas .getObjects() .findIndex( (obj) => obj.id === this.originalfabricObjects[this.originalfabricObjects.length - 1]?.id ) || 0; if (this.oldFill) { // 如果有旧填充,先获取旧的索引再移除旧填充 insertIndex = this.canvas.getObjects()?.findIndex((obj) => obj.id === this.oldFill?.id) || 0; removeCanvasObjectByObject(this.canvas, this.oldFill); } insertIndex = insertIndex == -1 ? 0 : insertIndex; insertObjectAtZIndex(this.canvas, this.newFill, insertIndex, false); // 插入到画布的指定位置 } this.canvas.renderAll(); // 实时更新的时候不生成缩略图 if (!this.isRetimeUpdate) { layer.fill = this.newFill.toObject(["id", "layerId"]); layer.fillColor = this.fillColor; this.canvasManager.thumbnailManager?.generateLayerThumbnail(this.layer.id); // 重新排序 // 使用LayerSort工具重新排列画布对象(如果可用) // await this?.canvasManager?.layerManager?.layerSort?.rearrangeObjects?.(); } return true; } async undo() { // this.group.removeWithUpdate(this.newFill); // this.canvas.remove(this.newFill); if (this.oldFill) { // 如果有旧填充,恢复旧填充 const { object } = findObjectById(this.canvas, this.oldFill.id); if (object) { this.newFill.set({ fill: this.oldFill.fill, left: this.oldFill.left, top: this.oldFill.top, width: this.oldFill.width, height: this.oldFill.height, }); this.newFill.setCoords(); } } else { // 如果没有旧填充,移除新填充 removeCanvasObjectByObject(this.canvas, this.layer.fill); } this.canvas.renderAll(); this.layer.fillColor = this.oldFillColor; this.layer.fill = this.oldFill; 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, }; } }