import { Command } from "./Command"; import { fabric } from "fabric-with-all"; /** * 创建背景图层命令 */ export class CreateBackgroundLayerCommand extends Command { constructor(options) { super({ name: "创建背景图层", saveState: true, }); this.canvas = options.canvas; this.layers = options.layers; this.backgroundLayer = options.backgroundLayer; this.canvasManager = options.canvasManager; this.historyManager = options.historyManager; this.beforeLayers = [...this.layers.value]; // 备份原图层列表 } execute() { // 检查是否已经存在背景图层 const existingBgLayer = this.layers.value.find( (layer) => layer.isBackground ); if (existingBgLayer) { console.warn("已存在背景层,不重复创建"); return existingBgLayer.id; } // 创建背景矩形对象 const bgObject = this._createBackgroundObject(); // 将背景对象添加到图层中 this.backgroundLayer.fabricObject = bgObject; // 添加图层到最底部 this.layers.value.push(this.backgroundLayer); // 添加到画布 this.canvas.add(bgObject); // 渲染画布 this.canvas.renderAll(); return this.backgroundLayer.id; } undo() { // 从图层列表中删除背景图层 const bgLayerIndex = this.layers.value.findIndex( (layer) => layer.isBackground ); if (bgLayerIndex !== -1) { this.layers.value.splice(bgLayerIndex, 1); } // 从画布中移除背景对象 if (this.backgroundLayer.fabricObject) { this.canvas.remove(this.backgroundLayer.fabricObject); } // 渲染画布 this.canvas.renderAll(); } /** * 创建背景矩形对象 * @returns {Object} fabric.js 矩形对象 * @private */ _createBackgroundObject() { // 计算画布尺寸 const canvasWidth = this.canvas.width; const canvasHeight = this.canvas.height; // 确保背景色为白色,如果没有设置或者是透明的话 const backgroundColor = this.backgroundLayer.backgroundColor && this.backgroundLayer.backgroundColor !== "transparent" ? this.backgroundLayer.backgroundColor : "#ffffff"; const rect = new fabric.Rect({ left: canvasWidth / 2, top: canvasHeight / 2, width: this.backgroundLayer.canvasWidth, height: this.backgroundLayer.canvasHeight, fill: backgroundColor, selectable: false, evented: false, hoverCursor: "default", id: `bg_object_${this.backgroundLayer.id}`, layerId: this.backgroundLayer.id, layerName: this.backgroundLayer.name, originX: "center", originY: "center", isBackground: true, // 标记为背景对象 }); return rect; } getInfo() { return { name: this.name, layerId: this.backgroundLayer.id, layerName: this.backgroundLayer.name, width: this.backgroundLayer.canvasWidth, height: this.backgroundLayer.canvasHeight, backgroundColor: this.backgroundLayer.backgroundColor, }; } } /** * 更新背景属性命令(如背景颜色) */ export class UpdateBackgroundCommand extends Command { constructor(options) { super({ name: "更新背景属性", saveState: true, }); this.canvas = options.canvas; this.layers = options.layers; this.backgroundColor = options.backgroundColor; this.historyManager = options.historyManager; // 查找背景图层 this.bgLayer = this.layers.value.find((layer) => layer.isBackground); this.oldBackgroundColor = this.bgLayer ? this.bgLayer.backgroundColor : "#ffffff"; } execute() { if (!this.bgLayer) { console.error("未找到背景图层"); return false; } // 更新背景图层属性 this.bgLayer.backgroundColor = this.backgroundColor; // 更新背景对象属性 if (this.bgLayer.fabricObject) { this.bgLayer.fabricObject.set("fill", this.backgroundColor); this.canvas.renderAll(); } return true; } undo() { if (!this.bgLayer) { return false; } // 恢复背景图层属性 this.bgLayer.backgroundColor = this.oldBackgroundColor; // 恢复背景对象属性 if (this.bgLayer.fabricObject) { this.bgLayer.fabricObject.set("fill", this.oldBackgroundColor); this.canvas.renderAll(); } return true; } getInfo() { return { name: this.name, layerId: this.bgLayer?.id, oldColor: this.oldBackgroundColor, newColor: this.backgroundColor, }; } } /** * 调整画布和背景大小命令 */ export class BackgroundSizeCommand extends Command { constructor(options) { super({ name: "调整背景大小", saveState: true, }); this.canvas = options.canvas; this.layers = options.layers; this.canvasManager = options.canvasManager; this.newWidth = options.newWidth; this.newHeight = options.newHeight; this.historyManager = options.historyManager; this.isRedGreenMode = options.isRedGreenMode; this.bgLayer = this.layers.value.find((layer) => layer.isBackground); // 记录原尺寸 this.oldWidth = this.bgLayer.fabricObject.width; this.oldHeight = this.bgLayer.fabricObject.height; // 查找背景图层 this.bgLayer = this.layers.value.find((layer) => layer.isBackground); } execute() { // 调整画布大小 this.canvas.setWidth(this.newWidth); this.canvas.setHeight(this.newHeight); console.log( `调整画布大小:${this.oldWidth}x${this.oldHeight} -> ${this.newWidth}x${this.newHeight}` ); // 如果使用 CanvasManager,通知它画布大小变化 if ( this.canvasManager && typeof this.canvasManager.updateCanvasSize === "function" ) { this.canvasManager.updateCanvasSize(this.newWidth, this.newHeight); } // 调整背景对象大小 if (this.bgLayer && this.bgLayer.fabricObject) { // 保持原有的背景颜色,如果没有设置则使用白色 const currentFill = this.bgLayer.fabricObject.fill || this.bgLayer.backgroundColor || "#ffffff"; this.bgLayer.fabricObject.set({ width: this.newWidth, height: this.newHeight, fill: currentFill, // 保持原有颜色 }); // 更新图层记录的尺寸 this.bgLayer.canvasWidth = this.newWidth; this.bgLayer.canvasHeight = this.newHeight; } // 渲染画布 this.canvas.renderAll(); return true; } undo() { // 恢复画布大小 this.canvas.setWidth(this.oldWidth); this.canvas.setHeight(this.oldHeight); // 如果使用 CanvasManager,通知它画布大小恢复 if ( this.canvasManager && typeof this.canvasManager.updateCanvasSize === "function" ) { this.canvasManager.updateCanvasSize(this.oldWidth, this.oldHeight); } // 恢复背景对象大小 if (this.bgLayer && this.bgLayer.fabricObject) { this.bgLayer.fabricObject.set({ width: this.oldWidth, height: this.oldHeight, }); // 恢复图层记录的尺寸 this.bgLayer.canvasWidth = this.oldWidth; this.bgLayer.canvasHeight = this.oldHeight; } // 渲染画布 this.canvas.renderAll(); return true; } getInfo() { return { name: this.name, oldWidth: this.oldWidth, oldHeight: this.oldHeight, newWidth: this.newWidth, newHeight: this.newHeight, }; } } /** * 调整背景大小并等比缩放所有其他元素的命令 */ export class BackgroundSizeWithScaleCommand extends Command { constructor(options) { super({ name: "调整背景大小并缩放元素", saveState: true, }); this.canvas = options.canvas; this.layers = options.layers; this.canvasManager = options.canvasManager; this.newWidth = options.newWidth; this.newHeight = options.newHeight; this.historyManager = options.historyManager; // 缩放策略:'uniform' | 'fill' | 'fit' | 'stretch' this.scaleStrategy = options.scaleStrategy || "uniform"; // 记录原尺寸 this.oldWidth = this.canvas.width; this.oldHeight = this.canvas.height; // 查找背景图层 this.bgLayer = this.layers.value.find((layer) => layer.isBackground); // 计算缩放比例 const scaleXRatio = this.newWidth / this.oldWidth; const scaleYRatio = this.newHeight / this.oldHeight; // 根据策略计算缩放比例和偏移 this._calculateScaleAndOffset(scaleXRatio, scaleYRatio); // 存储所有非背景对象的原始状态 this.objectStates = []; this._saveOriginalStates(); } /** * 保存所有非背景对象的原始状态 * @private */ _saveOriginalStates() { this.canvas.getObjects().forEach((obj) => { if (!obj.isBackground) { // 检查对象是否已经有原始状态记录 if (!obj._originalState) { // 第一次记录原始状态 obj._originalState = { left: obj.left, top: obj.top, scaleX: obj.scaleX || 1, scaleY: obj.scaleY || 1, width: obj.width, height: obj.height, // 记录基准画布尺寸 baseCanvasWidth: this.oldWidth, baseCanvasHeight: this.oldHeight, }; } this.objectStates.push({ obj: obj, // 使用原始状态而不是当前状态 left: obj._originalState.left, top: obj._originalState.top, scaleX: obj._originalState.scaleX, scaleY: obj._originalState.scaleY, width: obj._originalState.width, height: obj._originalState.height, }); } }); } /** * 根据缩放策略计算缩放比例和偏移量 * @param {number} scaleXRatio X轴缩放比例 * @param {number} scaleYRatio Y轴缩放比例 * @private */ _calculateScaleAndOffset(scaleXRatio, scaleYRatio) { switch (this.scaleStrategy) { case "uniform": // 统一缩放:使用平均值,保持相对比例的同时允许适度的形变 this.uniformScale = Math.sqrt(scaleXRatio * scaleYRatio); this.offsetX = (this.newWidth - this.oldWidth * this.uniformScale) / 2; this.offsetY = (this.newHeight - this.oldHeight * this.uniformScale) / 2; break; case "fit": // 适应模式:使用较小值,确保所有内容都在画布内,可能有留白 this.uniformScale = Math.min(scaleXRatio, scaleYRatio); this.offsetX = (this.newWidth - this.oldWidth * this.uniformScale) / 2; this.offsetY = (this.newHeight - this.oldHeight * this.uniformScale) / 2; break; case "fill": // 填充模式:使用较大值,填满画布,可能有部分内容被裁切 this.uniformScale = Math.max(scaleXRatio, scaleYRatio); this.offsetX = (this.newWidth - this.oldWidth * this.uniformScale) / 2; this.offsetY = (this.newHeight - this.oldHeight * this.uniformScale) / 2; break; case "stretch": // 拉伸模式:不保持宽高比,完全适应新尺寸 this.scaleX = scaleXRatio; this.scaleY = scaleYRatio; this.offsetX = 0; this.offsetY = 0; break; default: // 默认使用uniform模式 this.uniformScale = Math.sqrt(scaleXRatio * scaleYRatio); this.offsetX = (this.newWidth - this.oldWidth * this.uniformScale) / 2; this.offsetY = (this.newHeight - this.oldHeight * this.uniformScale) / 2; } } execute() { // 调整画布大小 this.canvas.setWidth(this.newWidth); this.canvas.setHeight(this.newHeight); // 如果使用 CanvasManager,通知它画布大小变化 if ( this.canvasManager && typeof this.canvasManager.updateCanvasSize === "function" ) { this.canvasManager.updateCanvasSize(this.newWidth, this.newHeight); } // 调整背景对象大小和位置 if (this.bgLayer && this.bgLayer.fabricObject) { // 保持原有的背景颜色,如果没有设置则使用白色 const currentFill = this.bgLayer.fabricObject.fill || this.bgLayer.backgroundColor || "#ffffff"; this.bgLayer.fabricObject.set({ width: this.newWidth, height: this.newHeight, left: this.newWidth / 2, top: this.newHeight / 2, fill: currentFill, // 保持原有颜色 }); // 更新图层记录的尺寸 this.bgLayer.canvasWidth = this.newWidth; this.bgLayer.canvasHeight = this.newHeight; } // 计算基于原始画布的缩放比例 const baseScaleX = this.newWidth / this.objectStates[0]?.obj._originalState?.baseCanvasWidth || this.newWidth / this.oldWidth; const baseScaleY = this.newHeight / this.objectStates[0]?.obj._originalState?.baseCanvasHeight || this.newHeight / this.oldHeight; // 根据策略缩放所有非背景对象 this.objectStates.forEach((state) => { const obj = state.obj; if (this.scaleStrategy === "stretch") { // 拉伸模式:使用不同的X和Y缩放比例 obj.set({ left: state.left * baseScaleX, top: state.top * baseScaleY, scaleX: state.scaleX * baseScaleX, scaleY: state.scaleY * baseScaleY, }); } else { // 其他模式:计算基于原始状态的统一缩放比例 const baseUniformScale = Math.sqrt(baseScaleX * baseScaleY); const baseOffsetX = (this.newWidth - (obj._originalState?.baseCanvasWidth || this.oldWidth) * baseUniformScale) / 2; const baseOffsetY = (this.newHeight - (obj._originalState?.baseCanvasHeight || this.oldHeight) * baseUniformScale) / 2; obj.set({ left: state.left * baseUniformScale + baseOffsetX, top: state.top * baseUniformScale + baseOffsetY, scaleX: state.scaleX * baseUniformScale, scaleY: state.scaleY * baseUniformScale, }); } obj.setCoords(); }); // 渲染画布 this.canvas.renderAll(); return true; } undo() { // 恢复画布大小 this.canvas.setWidth(this.oldWidth); this.canvas.setHeight(this.oldHeight); // 如果使用 CanvasManager,通知它画布大小恢复 if ( this.canvasManager && typeof this.canvasManager.updateCanvasSize === "function" ) { this.canvasManager.updateCanvasSize(this.oldWidth, this.oldHeight); } // 恢复背景对象大小和位置 if (this.bgLayer && this.bgLayer.fabricObject) { this.bgLayer.fabricObject.set({ width: this.oldWidth, height: this.oldHeight, left: this.oldWidth / 2, top: this.oldHeight / 2, }); // 恢复图层记录的尺寸 this.bgLayer.canvasWidth = this.oldWidth; this.bgLayer.canvasHeight = this.oldHeight; } // 恢复所有非背景对象的当前状态(而不是原始状态,因为可能有其他操作) this.objectStates.forEach((state) => { // 计算恢复到之前画布尺寸时的状态 const obj = state.obj; const originalState = obj._originalState; if (originalState) { const baseScaleX = this.oldWidth / originalState.baseCanvasWidth; const baseScaleY = this.oldHeight / originalState.baseCanvasHeight; const baseUniformScale = Math.sqrt(baseScaleX * baseScaleY); const baseOffsetX = (this.oldWidth - originalState.baseCanvasWidth * baseUniformScale) / 2; const baseOffsetY = (this.oldHeight - originalState.baseCanvasHeight * baseUniformScale) / 2; obj.set({ left: originalState.left * baseUniformScale + baseOffsetX, top: originalState.top * baseUniformScale + baseOffsetY, scaleX: originalState.scaleX * baseUniformScale, scaleY: originalState.scaleY * baseUniformScale, }); } else { // 降级到原来的逻辑 obj.set({ left: state.left, top: state.top, scaleX: state.scaleX, scaleY: state.scaleY, }); } obj.setCoords(); }); // 渲染画布 this.canvas.renderAll(); return true; } getInfo() { const info = { name: this.name, oldWidth: this.oldWidth, oldHeight: this.oldHeight, newWidth: this.newWidth, newHeight: this.newHeight, scaleStrategy: this.scaleStrategy, objectCount: this.objectStates.length, }; if (this.scaleStrategy === "stretch") { info.scaleX = this.scaleX; info.scaleY = this.scaleY; } else { info.uniformScale = this.uniformScale; info.offsetX = this.offsetX; info.offsetY = this.offsetY; } return info; } }