609 lines
17 KiB
JavaScript
609 lines
17 KiB
JavaScript
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;
|
||
}
|
||
}
|