Files
aida_front/src/component/Canvas/CanvasEditor/commands/BackgroundCommands.js
2025-06-29 23:29:47 +08:00

609 lines
17 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { findObjectById } from "../utils/helper";
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.toObject([
"id",
"layerId",
"type",
]);
// 添加图层到最底部
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.backgroundColorValue = options.backgroundColorValue; // 使用.value获取实际值
this.backgroundColor = options.backgroundColor; //
this.historyManager = options.historyManager;
// 查找背景图层
this.bgLayer = this.layers.value.find((layer) => layer.isBackground);
this.oldBackgroundColor = this.bgLayer.backgroundColor;
}
execute() {
if (!this.bgLayer) {
console.error("未找到背景图层");
return false;
}
// 更新背景图层属性
this.bgLayer.backgroundColor = this.backgroundColor;
// 更新背景对象属性
if (this.bgLayer.fabricObject) {
const { object } = findObjectById(
this.canvas,
this.bgLayer.fabricObject.id
);
object.set("fill", this.backgroundColor);
this.canvas.renderAll();
}
this.backgroundColorValue.value = this.backgroundColor; // 设置背景颜色
return true;
}
undo() {
if (!this.bgLayer) {
return false;
}
// 恢复背景图层属性
this.bgLayer.backgroundColor = this.oldBackgroundColor;
// 恢复背景对象属性
if (this.bgLayer.fabricObject) {
const { object } = findObjectById(
this.canvas,
this.bgLayer.fabricObject.id
);
object.set("fill", this.oldBackgroundColor);
this.canvas.renderAll();
}
this.backgroundColorValue.value = this.oldBackgroundColor; // 恢复背景颜色
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;
}
}