接入画布
This commit is contained in:
589
src/component/Canvas/CanvasEditor/commands/BackgroundCommands.js
Normal file
589
src/component/Canvas/CanvasEditor/commands/BackgroundCommands.js
Normal file
@@ -0,0 +1,589 @@
|
||||
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.oldWidth = this.canvas.width;
|
||||
this.oldHeight = this.canvas.height;
|
||||
|
||||
// 查找背景图层
|
||||
this.bgLayer = this.layers.value.find((layer) => layer.isBackground);
|
||||
}
|
||||
|
||||
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,
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user