import { Command } from "./Command"; import { findLayerRecursively } from "../utils/layerHelper"; import { fabric } from "fabric-with-all"; import { findObjectById, generateId, insertObjectAtZIndex, removeCanvasObjectByObject, createPatternTransform, } from "../utils/helper"; import { restoreFabricObject } from "../utils/objectHelper"; const scale = 0.3;// 默认缩放比例 export const FillSourceToBase64 = (source) => { if (source?.toDataURL) { return source.toDataURL?.(); } else if (source?.src) { return source.src; } return source; } /** * 填充图案平铺命令 * 填充重复属性:repeat | repeat-x | repeat-y | no-repeat * 默认缩放比例:0.3 * 默认偏移量:50% */ export class FillRepeatCommand extends Command { constructor(options) { super({ name: "填充图案平铺", saveState: true }); this.canvas = options.canvas; this.layers = options.layers; this.canvasManager = options.canvasManager; this.layerManager = options.layerManager; this.layerId = options.layerId; this.fillRepeat = options.fillRepeat; this.oldObjects = null; this.oldLocked = null; this.oldIsDisableUnlock = null; } async execute() { const { layer } = findLayerRecursively(this.layers.value, this.layerId); if (!layer || !layer.fabricObjects || layer.fabricObjects.length === 0) { console.warn("图层不存在或没有 fabric 对象"); return false; } const { object } = findObjectById(this.canvas, layer?.fabricObjects?.[0]?.id); if (!object || (object.type !== "rect" && object.type !== "image")) { console.warn("当前对象不能平铺", object.type); return false; } console.log("===========", object.toObject(["id", "layerId", "layerName"])) this.oldObjects = object; const img = await new Promise((resolve, reject) => { if (object.type === "rect") { let source = object.fill.source; resolve(source); } else if (object.type === "image") { // resolve(object.getElement()); // fabric.Image.fromURL( // object.src, // v => resolve(v), // { crossOrigin: "anonymous" } // ); const imgElement = object.getElement(); // 创建透明 Canvas const tcanvas = document.createElement('canvas'); tcanvas.width = imgElement.width; tcanvas.height = imgElement.height; const ctx = tcanvas.getContext('2d'); ctx.clearRect(0, 0, tcanvas.width, tcanvas.height); ctx.drawImage(imgElement, 0, 0); resolve(tcanvas); } }); const fill_ = { source: FillSourceToBase64(img), gapX: 0, gapY: 0, }; const bgObject = this.canvasManager.getBackgroundLayerObject(); const pattern = new fabric.Pattern({ source: img, repeat: this.fillRepeat, patternTransform: object.fill?.hasOwnProperty("patternTransform") ? object.fill.patternTransform : createPatternTransform(scale, 0), offsetX: object.fill?.hasOwnProperty("offsetX") ? object.fill.offsetX : bgObject.width / 2, // 水平偏移 offsetY: object.fill?.hasOwnProperty("offsetY") ? object.fill.offsetY : bgObject.height / 2, // 垂直偏移 }); const rect = new fabric.Rect({ id: object.id, layerId: object.layerId, layerName: object.layerName, fill_, }); layer.fabricObjects = [rect.toObject(["id", "layerId", "layerName"])]; this.oldLocked = layer.locked; // this.oldIsDisableUnlock = layer.isDisableUnlock; // layer.isDisableUnlock = true; if (this.oldObjects.type === "rect") { rect.set({ width: object.width, height: object.height, top: object.top, left: object.left, originX: object.originX, originY: object.originY, angle: object.angle, scaleX: object.scaleX, scaleY: object.scaleY, flipX: object.flipX, flipY: object.flipY, }); } else { rect.set({ width: bgObject.width, height: bgObject.height, top: bgObject.top, left: bgObject.left, originX: bgObject.originX, originY: bgObject.originY, }); layer.locked = true; } rect.set("fill", pattern); this.canvas.add(rect); this.canvas.remove(object); await this.layerManager?.updateLayersObjectsInteractivity(); await this.layerManager?.sortLayersWithTool?.(); await this.canvasManager.thumbnailManager?.generateLayerThumbnail( this.layerId ); await this.layerManager.selectLayerObjects(this.layerId); return true; } async undo() { if (!this.oldObjects) { console.warn("没有旧对象可恢复"); return false; } const { layer } = findLayerRecursively(this.layers.value, this.oldObjects.layerId); if (!layer || !layer.fabricObjects || layer.fabricObjects.length === 0) { console.warn("图层不存在或没有 fabric 对象"); return false; } const { object } = findObjectById(this.canvas, layer?.fabricObjects?.[0]?.id); this.canvas.remove(object); this.canvas.add(this.oldObjects); layer.fabricObjects = [this.oldObjects.toObject(["id", "layerId", "layerName"])]; layer.locked = this.oldLocked; // layer.isDisableUnlock = this.oldIsDisableUnlock; await this.layerManager?.updateLayersObjectsInteractivity(); await this.layerManager?.sortLayersWithTool?.(); this.canvas.renderAll(); this.canvasManager.thumbnailManager?.generateLayerThumbnail(this.layerId); return true; } } /** * 填充图案更改参数 */ export class FillRepeatChangeCommand extends Command { constructor(options) { super({ name: "填充图案更改参数", saveState: true }); this.canvas = options.canvas; this.layers = options.layers; this.canvasManager = options.canvasManager; this.layerManager = options.layerManager; this.layerId = options.layerId; this.newPattern = options.newPattern; this.oldPattern = null; } async execute() { const { layer } = findLayerRecursively(this.layers.value, this.layerId); if (!layer || !layer.fabricObjects || layer.fabricObjects.length === 0) { console.warn("图层不存在或没有 fabric 对象"); return false; } const { object } = findObjectById(this.canvas, layer?.fabricObjects?.[0]?.id); if (!object || object.type !== "rect") { console.warn("当前对象不是矩形", object); return false; } this.oldPattern = object.oldPattern || object.get("fill"); delete object.oldPattern; const oldPattern = { ...this.oldPattern }; delete oldPattern.id; const pattern = new fabric.Pattern({ ...oldPattern, ...this.newPattern, }); object.set("fill", pattern); this.canvas.renderAll(); return true; } async undo() { if (!this.oldPattern) { console.warn("没有旧图案可恢复"); return false; } const { layer } = findLayerRecursively(this.layers.value, this.layerId); if (!layer || !layer.fabricObjects || layer.fabricObjects.length === 0) { console.warn("图层不存在或没有 fabric 对象"); return false; } const { object } = findObjectById(this.canvas, layer?.fabricObjects?.[0]?.id); if (!object || object.type !== "rect") { console.warn("当前对象不是矩形", object); return false; } const pattern = new fabric.Pattern({ ...this.oldPattern }); object.set("fill", pattern); this.canvas.renderAll(); return true; } } /** * 填充图案更改间隙 */ export class FillRepeatGapChangeCommand extends Command { constructor(options) { super({ name: "填充图案更改间隙", saveState: true }); this.canvas = options.canvas; this.layers = options.layers; this.canvasManager = options.canvasManager; this.layerManager = options.layerManager; this.layerId = options.layerId; this.newGapX = options.newGapX; this.newGapY = options.newGapY; this.record = !!options.record; this.oldGapX = null; this.oldGapY = null; } async execute(isUndo = false) { const { layer } = findLayerRecursively(this.layers.value, this.layerId); if (!layer || !layer.fabricObjects || layer.fabricObjects.length === 0) { console.warn("图层不存在或没有 fabric 对象"); return false; } const { object } = findObjectById(this.canvas, layer?.fabricObjects?.[0]?.id); if (!object || object.type !== "rect") { console.warn("当前对象不是矩形", object); return false; } if (!object.fill_) { object.fill_ = { source: FillSourceToBase64(object.fill.source), gapX: 0, gapY: 0, } } if (isUndo) { object.fill_.gapX = this.oldGapX; object.fill_.gapY = this.oldGapY; } else { if (!object.oldFill_ && this.record) { object.oldFill_ = { ...object.fill_ }; } this.oldGapX = object.fill_.gapX; this.oldGapY = object.fill_.gapY; object.fill_.gapX = this.newGapX; object.fill_.gapY = this.newGapY; } const image = new Image(); image.src = object.fill_.source; await image.decode(); // 创建透明 Canvas const tcanvas = document.createElement('canvas'); tcanvas.width = image.width + object.fill_.gapX; tcanvas.height = image.height + object.fill_.gapY; const ctx = tcanvas.getContext('2d'); ctx.clearRect(0, 0, tcanvas.width, tcanvas.height); ctx.drawImage(image, 0, 0); const fill = object.get("fill"); fill.source = tcanvas; object.set("fill", new fabric.Pattern(fill)); this.canvas.renderAll(); return true; } async undo() { if (this.oldGapX === null || this.oldGapY === null) { console.warn("没有旧间隙可恢复"); return false; } await this.execute(true); return true; } }