接入画布

This commit is contained in:
X1627315083
2025-06-09 10:25:54 +08:00
parent 87a08f5f8f
commit c266967f16
157 changed files with 43833 additions and 1571 deletions

View 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;
}
}

View File

@@ -0,0 +1,698 @@
import { Command } from "./Command";
import { BrushStore } from "../store/BrushStore";
/**
* 笔刷属性设置基类命令
* 所有笔刷属性设置命令的基类
*/
class BaseBrushCommand extends Command {
constructor(options = {}) {
super(options);
this.brushStore = options.brushStore || BrushStore;
}
}
/**
* 笔刷大小设置命令
*/
export class BrushSizeCommand extends BaseBrushCommand {
/**
* @param {Object} options 命令选项
* @param {Number} options.size 要设置的笔刷大小
* @param {Number} options.previousSize 之前的笔刷大小(可选)
* @param {Object} options.brushStore BrushStore实例可选
*/
constructor(options = {}) {
super({
...options,
name: `设置笔刷大小: ${options.size}`,
description: `将笔刷大小从 ${options.previousSize || "?"} 设为 ${
options.size
}`,
});
this.size = options.size;
this.previousSize = options.previousSize || this.brushStore.state.size;
}
execute() {
// 记录当前大小用于撤销
if (this.previousSize === null) {
this.previousSize = this.brushStore.state.size;
}
// 执行设置
this.brushStore.setBrushSize(this.size);
return this.size;
}
undo() {
this.brushStore.setBrushSize(this.previousSize);
return this.previousSize;
}
}
/**
* 笔刷颜色设置命令
*/
export class BrushColorCommand extends BaseBrushCommand {
/**
* @param {Object} options 命令选项
* @param {String} options.color 要设置的颜色
* @param {String} options.previousColor 之前的颜色(可选)
* @param {Object} options.brushStore BrushStore实例可选
*/
constructor(options = {}) {
super({
...options,
name: `设置笔刷颜色: ${options.color}`,
description: `将笔刷颜色从 ${options.previousColor || "?"} 设为 ${
options.color
}`,
});
this.color = options.color;
this.previousColor = options.previousColor || this.brushStore.state.color;
}
execute() {
// 记录当前颜色用于撤销
if (this.previousColor === null) {
this.previousColor = this.brushStore.state.color;
}
// 执行设置
this.brushStore.setBrushColor(this.color);
return this.color;
}
undo() {
this.brushStore.setBrushColor(this.previousColor);
return this.previousColor;
}
}
/**
* 笔刷透明度设置命令
*/
export class BrushOpacityCommand extends BaseBrushCommand {
/**
* @param {Object} options 命令选项
* @param {Number} options.opacity 要设置的透明度 (0-1)
* @param {Number} options.previousOpacity 之前的透明度(可选)
* @param {Object} options.brushStore BrushStore实例可选
*/
constructor(options = {}) {
super({
...options,
name: `设置笔刷透明度: ${options.opacity}`,
description: `将笔刷透明度从 ${options.previousOpacity || "?"} 设为 ${
options.opacity
}`,
});
this.opacity = options.opacity;
this.previousOpacity =
options.previousOpacity || this.brushStore.state.opacity;
}
execute() {
// 记录当前透明度用于撤销
if (this.previousOpacity === null) {
this.previousOpacity = this.brushStore.state.opacity;
}
// 执行设置
this.brushStore.setBrushOpacity(this.opacity);
return this.opacity;
}
undo() {
this.brushStore.setBrushOpacity(this.previousOpacity);
return this.previousOpacity;
}
}
/**
* 笔刷类型设置命令
*/
export class BrushTypeCommand extends BaseBrushCommand {
/**
* @param {Object} options 命令选项
* @param {String} options.brushType 要设置的笔刷类型
* @param {String} options.previousType 之前的笔刷类型(可选)
* @param {Object} options.brushStore BrushStore实例可选
*/
constructor(options = {}) {
super({
...options,
name: `设置笔刷类型: ${options.brushType}`,
description: `将笔刷类型从 ${options.previousType || "?"} 设为 ${
options.brushType
}`,
});
this.brushType = options.brushType;
this.previousType = options.previousType || this.brushStore.state.type;
this.brushManager = options.brushManager;
}
execute() {
// 记录当前类型用于撤销
if (this.previousType === null) {
this.previousType = this.brushStore.state.type;
}
// 执行设置
// this.brushStore.setBrushType(this.brushType);
this.brushManager.setBrushType(this.brushType);
return this.brushType;
}
undo() {
// this.brushStore.setBrushType(this.previousType);
this.brushManager.setBrushType(this.previousType);
return this.previousType;
}
}
/**
* 材质设置命令
*/
export class TextureCommand extends BaseBrushCommand {
/**
* @param {Object} options 命令选项
* @param {Boolean} options.enabled 是否启用材质
* @param {String} options.path 材质路径
* @param {Number} options.scale 材质缩放
* @param {Object} options.previous 之前的材质设置(可选)
* @param {Object} options.brushStore BrushStore实例可选
*/
constructor(options = {}) {
super({
...options,
name: options.enabled ? "启用笔刷材质" : "禁用笔刷材质",
description: options.enabled
? `启用材质: ${options.path || "[默认]"}`
: "禁用笔刷材质",
});
this.enabled = options.enabled;
this.path = options.path;
this.scale = options.scale;
// 保存之前状态用于撤销
this.previous = options.previous || {
enabled: this.brushStore.state.textureEnabled,
path: this.brushStore.state.texturePath,
scale: this.brushStore.state.textureScale,
};
}
execute() {
// 记录当前状态用于撤销
if (!this.previous) {
this.previous = {
enabled: this.brushStore.state.textureEnabled,
path: this.brushStore.state.texturePath,
scale: this.brushStore.state.textureScale,
};
}
// 执行设置
this.brushStore.setTextureEnabled(this.enabled);
if (this.path) {
this.brushStore.setTexturePath(this.path);
}
if (this.scale !== undefined) {
this.brushStore.setTextureScale(this.scale);
}
return {
enabled: this.enabled,
path: this.path,
scale: this.scale,
};
}
undo() {
if (!this.previous) return null;
this.brushStore.setTextureEnabled(this.previous.enabled);
this.brushStore.setTexturePath(this.previous.path);
this.brushStore.setTextureScale(this.previous.scale);
return this.previous;
}
}
/**
* 预设应用命令
*/
export class BrushPresetCommand extends BaseBrushCommand {
/**
* @param {Object} options 命令选项
* @param {Number|Object} options.preset 预设索引或预设对象
* @param {Object} options.previousState 之前的状态(可选)
* @param {Object} options.brushStore BrushStore实例可选
*/
constructor(options = {}) {
const presetName =
typeof options.preset === "object"
? options.preset.name
: `预设 ${options.preset}`;
super({
...options,
name: `应用笔刷预设: ${presetName}`,
description: `应用预设: ${presetName}`,
});
this.preset = options.preset;
// 保存之前状态用于撤销
this.previousState = options.previousState || {
size: this.brushStore.state.size,
color: this.brushStore.state.color,
opacity: this.brushStore.state.opacity,
type: this.brushStore.state.type,
textureEnabled: this.brushStore.state.textureEnabled,
textureScale: this.brushStore.state.textureScale,
texturePath: this.brushStore.state.texturePath,
};
}
execute() {
// 记录当前状态用于撤销
if (!this.previousState) {
this.previousState = {
size: this.brushStore.state.size,
color: this.brushStore.state.color,
opacity: this.brushStore.state.opacity,
type: this.brushStore.state.type,
textureEnabled: this.brushStore.state.textureEnabled,
textureScale: this.brushStore.state.textureScale,
texturePath: this.brushStore.state.texturePath,
};
}
// 应用预设
if (typeof this.preset === "number") {
this.brushStore.applyPreset(this.preset);
} else if (typeof this.preset === "object") {
// 应用自定义预设对象
if (this.preset.size !== undefined)
this.brushStore.setBrushSize(this.preset.size);
if (this.preset.color !== undefined)
this.brushStore.setBrushColor(this.preset.color);
if (this.preset.opacity !== undefined)
this.brushStore.setBrushOpacity(this.preset.opacity);
if (this.preset.type !== undefined)
this.brushStore.setBrushType(this.preset.type);
if (this.preset.textureEnabled !== undefined) {
this.brushStore.setTextureEnabled(this.preset.textureEnabled);
}
if (this.preset.texturePath !== undefined) {
this.brushStore.setTexturePath(this.preset.texturePath);
}
if (this.preset.textureScale !== undefined) {
this.brushStore.setTextureScale(this.preset.textureScale);
}
}
return true;
}
undo() {
if (!this.previousState) return false;
// 恢复之前的状态
this.brushStore.setBrushSize(this.previousState.size);
this.brushStore.setBrushColor(this.previousState.color);
this.brushStore.setBrushOpacity(this.previousState.opacity);
this.brushStore.setBrushType(this.previousState.type);
this.brushStore.setTextureEnabled(this.previousState.textureEnabled);
this.brushStore.setTextureScale(this.previousState.textureScale);
this.brushStore.setTexturePath(this.previousState.texturePath);
return true;
}
}
/**
* 笔刷属性命令
* 用于修改笔刷的任意属性,包括特殊属性
*/
export class BrushPropertyCommand extends Command {
/**
* 构造函数
* @param {Object} options 命令选项
* @param {String} options.propertyId 属性ID
* @param {any} options.value 新的属性值
* @param {Object} options.brushStore BrushStore实例
*/
constructor(options) {
super({
name: "笔刷属性更改",
description: `更改笔刷属性 ${options.propertyId}`,
options,
});
this.propertyId = options.propertyId;
this.newValue = options.value;
this.oldValue = null;
this.brushStore = options.brushStore || BrushStore;
}
/**
* 执行命令
* @returns {Boolean} 是否执行成功
*/
execute() {
if (!this.brushStore) {
console.error("BrushStore不可用");
return false;
}
// 保存旧值用于撤销
this.oldValue = this.brushStore.getPropertyValue(this.propertyId);
// 更新属性值
this.brushStore.updatePropertyValue(this.propertyId, this.newValue);
return true;
}
/**
* 撤销命令
* @param {Object} context 命令上下文
* @returns {Boolean} 是否撤销成功
*/
undo() {
if (!this.brushStore || this.oldValue === null) {
return false;
}
// 恢复旧值
this.brushStore.updatePropertyValue(this.propertyId, this.oldValue);
return true;
}
}
/**
* 材质选择命令
*/
export class TextureSelectionCommand extends BaseBrushCommand {
/**
* @param {Object} options 命令选项
* @param {String} options.textureId 要设置的材质ID
* @param {String} options.previousTextureId 之前的材质ID可选
* @param {Object} options.brushManager 笔刷管理器实例
*/
constructor(options = {}) {
super({
...options,
name: `切换纹理材质`,
description: `切换到材质: ${options.textureId}`,
});
this.textureId = options.textureId;
this.previousTextureId = options.previousTextureId;
this.brushManager = options.brushManager;
}
execute() {
// 记录当前材质用于撤销
const currentBrush = this.brushManager.activeBrush;
if (currentBrush && currentBrush.getCurrentTexture) {
const currentTexture = currentBrush.getCurrentTexture();
this.previousTextureId = currentTexture ? currentTexture.id : null;
}
// 确保当前是材质笔刷
if (this.brushManager.getCurrentBrushType() !== "texture") {
this.brushManager.setBrushType("texture");
}
// 设置材质
const activeBrush = this.brushManager.activeBrush;
if (activeBrush && activeBrush.setTextureById) {
activeBrush.setTextureById(this.textureId);
}
return this.textureId;
}
undo() {
if (!this.previousTextureId) return false;
// 确保当前是材质笔刷
if (this.brushManager.getCurrentBrushType() !== "texture") {
this.brushManager.setBrushType("texture");
}
// 恢复之前的材质
const activeBrush = this.brushManager.activeBrush;
if (activeBrush && activeBrush.setTextureById) {
activeBrush.setTextureById(this.previousTextureId);
}
return this.previousTextureId;
}
}
/**
* 材质属性设置命令
*/
export class TexturePropertyCommand extends BaseBrushCommand {
/**
* @param {Object} options 命令选项
* @param {String} options.property 要设置的属性名称 (scale, rotation, offsetX, offsetY等)
* @param {any} options.value 要设置的属性值
* @param {any} options.previousValue 之前的属性值(可选)
* @param {Object} options.brushManager 笔刷管理器实例
*/
constructor(options = {}) {
super({
...options,
name: `设置材质属性: ${options.property}`,
description: `将材质${options.property}${
options.previousValue || "?"
} 设为 ${options.value}`,
});
this.property = options.property;
this.value = options.value;
this.previousValue = options.previousValue;
this.brushManager = options.brushManager;
}
execute() {
// 确保当前是材质笔刷
if (this.brushManager.getCurrentBrushType() !== "texture") {
this.brushManager.setBrushType("texture");
}
const activeBrush = this.brushManager.activeBrush;
if (!activeBrush || !activeBrush.setTextureProperty) {
return false;
}
// 记录当前值用于撤销
if (this.previousValue === undefined) {
this.previousValue = activeBrush.getTextureProperty(this.property);
}
// 设置新值
activeBrush.setTextureProperty(this.property, this.value);
return this.value;
}
undo() {
if (this.previousValue === undefined) return false;
// 确保当前是材质笔刷
if (this.brushManager.getCurrentBrushType() !== "texture") {
this.brushManager.setBrushType("texture");
}
const activeBrush = this.brushManager.activeBrush;
if (activeBrush && activeBrush.setTextureProperty) {
activeBrush.setTextureProperty(this.property, this.previousValue);
}
return this.previousValue;
}
}
/**
* 材质预设应用命令
*/
export class TexturePresetCommand extends BaseBrushCommand {
/**
* @param {Object} options 命令选项
* @param {String|Object} options.preset 预设ID或预设对象
* @param {Object} options.previousState 之前的材质状态(可选)
* @param {Object} options.brushManager 笔刷管理器实例
*/
constructor(options = {}) {
const presetName =
typeof options.preset === "object" ? options.preset.name : options.preset;
super({
...options,
name: `应用材质预设: ${presetName}`,
description: `应用材质预设: ${presetName}`,
});
this.preset = options.preset;
this.previousState = options.previousState;
this.brushManager = options.brushManager;
}
execute() {
// 确保当前是材质笔刷
if (this.brushManager.getCurrentBrushType() !== "texture") {
this.brushManager.setBrushType("texture");
}
const activeBrush = this.brushManager.activeBrush;
if (!activeBrush || !activeBrush.applyTexturePreset) {
return false;
}
// 记录当前状态用于撤销
if (!this.previousState) {
this.previousState = activeBrush.getCurrentTextureState();
}
// 应用预设
activeBrush.applyTexturePreset(this.preset);
return true;
}
undo() {
if (!this.previousState) return false;
// 确保当前是材质笔刷
if (this.brushManager.getCurrentBrushType() !== "texture") {
this.brushManager.setBrushType("texture");
}
const activeBrush = this.brushManager.activeBrush;
if (activeBrush && activeBrush.restoreTextureState) {
activeBrush.restoreTextureState(this.previousState);
}
return true;
}
}
/**
* 纹理上传命令
*/
export class TextureUploadCommand extends BaseBrushCommand {
/**
* @param {Object} options 命令选项
* @param {File} options.file 要上传的纹理文件
* @param {String} options.name 纹理名称(可选)
* @param {String} options.category 纹理分类(可选)
* @param {Object} options.texturePresetManager 纹理预设管理器实例
* @param {Object} options.brushManager 笔刷管理器实例
*/
constructor(options = {}) {
super({
...options,
name: `上传纹理: ${options.name || options.file?.name || '未知'}`,
description: `上传自定义纹理文件`,
});
this.file = options.file;
this.name = options.name || options.file?.name?.replace(/\.[^/.]+$/, "") || "自定义纹理";
this.category = options.category || "自定义材质";
this.texturePresetManager = options.texturePresetManager;
this.brushManager = options.brushManager;
this.uploadedTextureId = null;
}
async execute() {
if (!this.file || !this.texturePresetManager) {
throw new Error('缺少必要的文件或纹理预设管理器');
}
try {
// 创建文件 data URL
const dataUrl = await this._fileToDataUrl(this.file);
// 添加到纹理预设管理器
this.uploadedTextureId = this.texturePresetManager.addCustomTexture({
name: this.name,
category: this.category,
file: this.file,
dataUrl: dataUrl,
preview: dataUrl,
});
// 如果是纹理笔刷,自动应用新上传的纹理
if (this.brushManager) {
if (this.brushManager.getCurrentBrushType() !== "texture") {
this.brushManager.setBrushType("texture");
}
const activeBrush = this.brushManager.activeBrush;
if (activeBrush && activeBrush.updateProperty) {
activeBrush.updateProperty("textureSelector", this.uploadedTextureId);
}
}
return {
textureId: this.uploadedTextureId,
dataUrl: dataUrl,
name: this.name
};
} catch (error) {
console.error('纹理上传失败:', error);
throw new Error(`纹理上传失败: ${error.message}`);
}
}
undo() {
if (!this.uploadedTextureId || !this.texturePresetManager) {
return false;
}
// 从纹理预设管理器中移除上传的纹理
try {
this.texturePresetManager.removeCustomTexture(this.uploadedTextureId);
return true;
} catch (error) {
console.error('撤销纹理上传失败:', error);
return false;
}
}
/**
* 将文件转换为 data URL
* @private
* @param {File} file
* @returns {Promise<string>}
*/
_fileToDataUrl(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (event) => {
resolve(event.target.result);
};
reader.onerror = (error) => {
reject(error);
};
reader.readAsDataURL(file);
});
}
}

View File

@@ -0,0 +1,296 @@
/**
* 基础命令类
* 所有命令都应该继承这个类
*/
export class Command {
constructor(options = {}) {
this.name = options.name || "未命名命令";
this.description = options.description || "";
this.undoable = options.undoable !== false; // 默认可撤销
this.timestamp = Date.now();
}
/**
* 执行命令
* @returns {*} 执行结果可以是Promise
*/
execute() {
throw new Error("子类必须实现execute方法");
}
/**
* 撤销命令
* @returns {*} 撤销结果可以是Promise
*/
undo() {
if (!this.undoable) {
throw new Error("此命令不支持撤销");
}
throw new Error("可撤销命令必须实现undo方法");
}
/**
* 获取命令信息
*/
getInfo() {
return {
name: this.name,
description: this.description,
undoable: this.undoable,
timestamp: this.timestamp,
};
}
}
/**
* 复合命令类
* 用于批量执行多个命令,替代事务系统
*/
export class CompositeCommand extends Command {
constructor(commands = [], options = {}) {
super({
name: options.name || "复合命令",
description: options.description || "批量执行多个命令",
...options,
});
this.commands = Array.isArray(commands) ? commands : [];
this.executedCommands = []; // 记录已执行的命令,用于撤销
this.isExecuting = false;
}
/**
* 添加子命令
*/
addCommand(command) {
if (!command || typeof command.execute !== "function") {
throw new Error("无效的命令对象");
}
this.commands.push(command);
return this;
}
/**
* 批量添加子命令
*/
addCommands(commands) {
if (Array.isArray(commands)) {
commands.forEach((cmd) => this.addCommand(cmd));
}
return this;
}
/**
* 执行所有子命令(串行执行)
*/
async execute() {
if (this.isExecuting) {
throw new Error("复合命令正在执行中");
}
this.isExecuting = true;
this.executedCommands = [];
try {
const results = [];
// 串行执行所有子命令
for (const command of this.commands) {
try {
console.log(`📦 复合命令执行子命令: ${command.constructor.name}`);
const result = command.execute();
// 如果是异步命令,等待完成
const finalResult = this._isPromise(result) ? await result : result;
results.push(finalResult);
this.executedCommands.push(command);
console.log(`✅ 子命令执行成功: ${command.constructor.name}`);
} catch (error) {
console.error(
`❌ 子命令执行失败: ${command.constructor.name}`,
error
);
// 执行失败时,撤销已执行的命令
await this._rollbackExecutedCommands();
throw new Error(`复合命令执行失败:${error.message}`);
}
}
this.isExecuting = false;
console.log(`✅ 复合命令执行完成,共执行 ${results.length} 个子命令`);
return results;
} catch (error) {
this.isExecuting = false;
throw error;
}
}
/**
* 撤销所有已执行的子命令(逆序撤销)
*/
async undo() {
if (this.isExecuting) {
throw new Error("复合命令正在执行中,无法撤销");
}
if (this.executedCommands.length === 0) {
console.warn("没有已执行的子命令需要撤销");
return true;
}
console.log(
`↩️ 开始撤销复合命令,共 ${this.executedCommands.length} 个子命令`
);
try {
// 逆序撤销已执行的命令
const commands = [...this.executedCommands].reverse();
const results = [];
for (const command of commands) {
if (typeof command.undo === "function") {
try {
console.log(`↩️ 撤销子命令: ${command.constructor.name}`);
const result = command.undo();
// 如果是异步撤销,等待完成
const finalResult = this._isPromise(result) ? await result : result;
results.push(finalResult);
console.log(`✅ 子命令撤销成功: ${command.constructor.name}`);
} catch (error) {
console.error(
`❌ 子命令撤销失败: ${command.constructor.name}`,
error
);
// 撤销失败不中断整个撤销过程,但要记录错误
}
} else {
console.warn(`⚠️ 子命令不支持撤销: ${command.constructor.name}`);
}
}
this.executedCommands = [];
console.log(`✅ 复合命令撤销完成`);
return results;
} catch (error) {
console.error("❌ 复合命令撤销过程中发生错误:", error);
throw error;
}
}
/**
* 回滚已执行的命令(内部使用)
* @private
*/
async _rollbackExecutedCommands() {
console.log(`🔄 开始回滚已执行的 ${this.executedCommands.length} 个子命令`);
const commands = [...this.executedCommands].reverse();
for (const command of commands) {
if (typeof command.undo === "function") {
try {
console.log(`🔄 回滚子命令: ${command.constructor.name}`);
const result = command.undo();
if (this._isPromise(result)) {
await result;
}
console.log(`✅ 子命令回滚成功: ${command.constructor.name}`);
} catch (error) {
console.error(
`❌ 子命令回滚失败: ${command.constructor.name}`,
error
);
// 回滚失败不中断整个回滚过程
}
}
}
this.executedCommands = [];
console.log(`✅ 回滚完成`);
}
/**
* 检查返回值是否为Promise
* @private
*/
_isPromise(value) {
return (
value &&
typeof value === "object" &&
typeof value.then === "function" &&
typeof value.catch === "function"
);
}
/**
* 获取复合命令信息
*/
getInfo() {
return {
...super.getInfo(),
commandCount: this.commands.length,
executedCount: this.executedCommands.length,
isExecuting: this.isExecuting,
subCommands: this.commands.map((cmd) =>
cmd.getInfo ? cmd.getInfo() : cmd.constructor.name
),
};
}
}
/**
* 函数命令包装器
* 将普通函数包装为命令对象
*/
export class FunctionCommand extends Command {
constructor(executeFn, undoFn = null, options = {}) {
super({
name: options.name || "函数命令",
undoable: typeof undoFn === "function",
...options,
});
if (typeof executeFn !== "function") {
throw new Error("执行函数不能为空");
}
this.executeFn = executeFn;
this.undoFn = undoFn;
this.executeResult = null; // 保存执行结果用于撤销
}
async execute() {
const result = this.executeFn();
this.executeResult = this._isPromise(result) ? await result : result;
return this.executeResult;
}
async undo() {
if (!this.undoFn) {
throw new Error("此函数命令不支持撤销");
}
const result = this.undoFn(this.executeResult);
return this._isPromise(result) ? await result : result;
}
/**
* 检查返回值是否为Promise
* @private
*/
_isPromise(value) {
return (
value &&
typeof value === "object" &&
typeof value.then === "function" &&
typeof value.catch === "function"
);
}
}

View File

@@ -0,0 +1,491 @@
import { CompositeCommand } from "./Command.js";
import { AddLayerCommand, CreateImageLayerCommand } from "./LayerCommands.js";
import { ToolCommand } from "./ToolCommands.js";
import { ClearSelectionCommand } from "./SelectionCommands.js";
import { createLayer, LayerType, OperationType } from "../utils/layerHelper.js";
//import { fabric } from "fabric-with-all";
/**
* 套索抠图命令
* 实现将选区内容抠图到新图层的功能
*/
export class LassoCutoutCommand extends CompositeCommand {
constructor(options = {}) {
super([], {
name: "套索抠图",
description: "将选区抠图到新图层",
});
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.selectionManager = options.selectionManager;
this.toolManager = options.toolManager;
this.sourceLayerId = options.sourceLayerId;
this.newLayerName = options.newLayerName || "抠图";
this.newLayerId = null;
this.cutoutImageUrl = null;
this.fabricImage = null;
this.executedCommands = [];
// 高清截图选项
this.highResolutionEnabled = options.highResolutionEnabled !== false; // 默认启用
this.baseResolutionScale = options.baseResolutionScale || 4; // 基础分辨率倍数提高到4倍获得更清晰的图像
}
async execute() {
if (!this.canvas || !this.layerManager || !this.selectionManager) {
console.error("无法执行套索抠图:参数无效");
return false;
}
try {
this.executedCommands = [];
// 获取选区
const selectionObject = this.selectionManager.getSelectionObject();
if (!selectionObject) {
console.error("无法执行套索抠图:当前没有选区");
return false;
}
// 确定源图层
const sourceLayer = this.layerManager.getActiveLayer();
if (!sourceLayer || sourceLayer.fabricObjects.length === 0) {
console.error("无法执行套索抠图:源图层无效");
return false;
}
// 获取选区边界信息用于后续定位
const selectionBounds = selectionObject.getBoundingRect(true, true);
// 执行在当前画布上的抠图操作
this.cutoutImageUrl = await this._performCutout(
sourceLayer,
selectionObject
);
if (!this.cutoutImageUrl) {
console.error("抠图失败");
return false;
}
// 创建fabric图像对象传递选区边界信息
this.fabricImage = await this._createFabricImage(
this.cutoutImageUrl,
selectionBounds
);
if (!this.fabricImage) {
console.error("创建图像对象失败");
return false;
}
// 1. 创建图像图层命令
const createImageLayerCmd = new CreateImageLayerCommand({
layerManager: this.layerManager,
fabricImage: this.fabricImage,
toolManager: this.toolManager,
layerName: this.newLayerName,
});
// 执行创建图像图层命令
const result = await createImageLayerCmd.execute();
this.newLayerId = createImageLayerCmd.newLayerId;
this.executedCommands.push(createImageLayerCmd);
// 2. 清除选区命令
const clearSelectionCmd = new ClearSelectionCommand({
canvas: this.canvas,
selectionManager: this.selectionManager,
});
// 执行清除选区命令
await clearSelectionCmd.execute();
this.executedCommands.push(clearSelectionCmd);
console.log(`套索抠图完成新图层ID: ${this.newLayerId}`);
return {
newLayerId: this.newLayerId,
cutoutImageUrl: this.cutoutImageUrl,
};
} catch (error) {
console.error("套索抠图过程中出错:", error);
// 如果已经创建了新图层,需要进行清理
if (this.newLayerId) {
try {
await this.layerManager.removeLayer(this.newLayerId);
} catch (cleanupError) {
console.warn("清理新图层失败:", cleanupError);
}
}
throw error;
}
}
async undo() {
try {
// 逆序撤销所有已执行的命令
for (let i = this.executedCommands.length - 1; i >= 0; i--) {
const command = this.executedCommands[i];
if (command && typeof command.undo === "function") {
await command.undo();
}
}
this.executedCommands = [];
this.newLayerId = null;
this.cutoutImageUrl = null;
this.fabricImage = null;
return true;
} catch (error) {
console.error("撤销套索抠图失败:", error);
return false;
}
}
/**
* 在当前画布上执行抠图操作
* @param {Object} sourceLayer 源图层
* @param {Object} selectionObject 选区对象
* @returns {String} 抠图结果的DataURL
* @private
*/
async _performCutout(sourceLayer, selectionObject) {
try {
console.log("=== 开始在当前画布执行抠图 ===");
// 获取选区边界
const selectionBounds = selectionObject.getBoundingRect(true, true);
console.log(
`选区边界: left=${selectionBounds.left}, top=${selectionBounds.top}, width=${selectionBounds.width}, height=${selectionBounds.height}`
);
// 保存画布当前状态
const originalActiveObject = this.canvas.getActiveObject();
const originalSelection = this.canvas.selection;
// 临时禁用画布选择
this.canvas.selection = false;
this.canvas.discardActiveObject();
let tempGroup = null;
let originalObjects = [];
try {
// 收集源图层中的可见对象
const visibleObjects = sourceLayer.fabricObjects.filter(
(obj) => obj.visible
);
console.log(`源图层可见对象数量: ${visibleObjects.length}`);
if (visibleObjects.length === 0) {
throw new Error("源图层没有可见对象");
}
// 如果只有一个对象且已经是组,直接使用
if (visibleObjects.length === 1 && visibleObjects[0].type === "group") {
tempGroup = visibleObjects[0];
console.log("使用现有组对象");
} else {
// 创建临时组
console.log("创建临时组...");
// 记录原始对象的位置和状态,用于后续恢复
originalObjects = visibleObjects.map((obj) => ({
object: obj,
originalLeft: obj.left,
originalTop: obj.top,
originalAngle: obj.angle,
originalScaleX: obj.scaleX,
originalScaleY: obj.scaleY,
}));
// 不需要从画布移除原对象,直接创建组
// 克隆对象来创建组,避免影响原对象
const clonedObjects = [];
for (const obj of visibleObjects) {
const cloned = await this._cloneObject(obj);
clonedObjects.push(cloned);
}
// 创建组
tempGroup = new fabric.Group(clonedObjects, {
selectable: false,
evented: false,
});
// 添加组到画布
this.canvas.add(tempGroup);
}
// 设置选区为裁剪路径
const clipPath = await this._cloneObject(selectionObject);
clipPath.set({
fill: "",
stroke: "",
absolutePositioned: true,
originX: "left",
originY: "top",
});
// 应用裁剪路径到组
tempGroup.set({
clipPath: clipPath,
});
this.canvas.renderAll();
// 计算渲染区域
const renderBounds = {
left: selectionBounds.left,
top: selectionBounds.top,
width: selectionBounds.width,
height: selectionBounds.height,
};
// 设置高分辨率倍数,用于提高图像清晰度
let highResolutionScale = 1;
if (this.highResolutionEnabled) {
// 结合设备像素比和配置的基础倍数,确保在所有设备上都有最佳效果
const devicePixelRatio = window.devicePixelRatio || 1;
// 使用更激进的缩放策略,确保高清晰度
highResolutionScale = Math.max(
this.baseResolutionScale,
devicePixelRatio * 2
);
console.log(
`设备像素比: ${devicePixelRatio}, 基础倍数: ${this.baseResolutionScale}, 最终放大倍数: ${highResolutionScale}`
);
} else {
console.log("高分辨率渲染已禁用使用1x倍数");
}
// 创建用于导出的高分辨率canvas
const exportCanvas = document.createElement("canvas");
const exportCtx = exportCanvas.getContext("2d", {
alpha: true,
willReadFrequently: false,
colorSpace: "srgb",
});
// 设置canvas的实际像素尺寸放大倍数
const actualWidth = Math.round(
renderBounds.width * highResolutionScale
);
const actualHeight = Math.round(
renderBounds.height * highResolutionScale
);
exportCanvas.width = actualWidth;
exportCanvas.height = actualHeight;
// 设置canvas的显示尺寸CSS尺寸保持与选区一致
exportCanvas.style.width = renderBounds.width + "px";
exportCanvas.style.height = renderBounds.height + "px";
// 启用最高质量渲染设置
exportCtx.imageSmoothingEnabled = true;
exportCtx.imageSmoothingQuality = "high";
// 设置文本渲染质量
if (exportCtx.textRenderingOptimization) {
exportCtx.textRenderingOptimization = "optimizeQuality";
}
// 设置线条和图形的渲染质量
exportCtx.lineCap = "round";
exportCtx.lineJoin = "round";
exportCtx.miterLimit = 10;
// 设置画布背景为透明
exportCtx.clearRect(0, 0, actualWidth, actualHeight);
// 获取画布当前的变换矩阵
const vpt = this.canvas.viewportTransform;
const zoom = this.canvas.getZoom();
// 保存当前变换状态
exportCtx.save();
// 应用高分辨率缩放
exportCtx.scale(highResolutionScale, highResolutionScale);
// 应用偏移,只渲染选区部分
exportCtx.translate(-renderBounds.left, -renderBounds.top);
// 如果画布有缩放和平移,需要应用相应变换
if (zoom !== 1 || vpt[4] !== 0 || vpt[5] !== 0) {
exportCtx.transform(vpt[0], vpt[1], vpt[2], vpt[3], vpt[4], vpt[5]);
}
// 渲染被裁剪的组
tempGroup.render(exportCtx);
exportCtx.restore();
// 获取结果 - 使用最高质量设置
const dataUrl = exportCanvas.toDataURL("image/png", 1.0);
console.log(
`抠图完成,选区尺寸: ${renderBounds.width}x${renderBounds.height}, 实际渲染尺寸: ${actualWidth}x${actualHeight}, 放大倍数: ${highResolutionScale}x`
);
return dataUrl;
} finally {
// 清理和恢复
if (tempGroup) {
// 移除裁剪路径
tempGroup.set({ clipPath: null });
// 如果是我们创建的临时组,需要移除它
if (originalObjects.length > 0) {
this.canvas.remove(tempGroup);
// 恢复原始对象的状态(位置等信息保持不变)
originalObjects.forEach(
({
object,
originalLeft,
originalTop,
originalAngle,
originalScaleX,
originalScaleY,
}) => {
// 确保对象仍然在画布上且状态正确
if (!this.canvas.getObjects().includes(object)) {
this.canvas.add(object);
}
// 恢复原始变换状态(如果需要的话)
object.set({
left: originalLeft,
top: originalTop,
angle: originalAngle,
scaleX: originalScaleX,
scaleY: originalScaleY,
});
object.setCoords();
}
);
}
}
// 恢复画布状态
this.canvas.selection = originalSelection;
if (originalActiveObject) {
this.canvas.setActiveObject(originalActiveObject);
}
this.canvas.renderAll();
}
} catch (error) {
console.error("在当前画布执行抠图失败:", error);
return null;
}
}
/**
* 从DataURL创建fabric图像对象
* @param {String} dataUrl 图像DataURL
* @param {Object} selectionBounds 选区边界信息
* @returns {fabric.Image} fabric图像对象
* @private
*/
async _createFabricImage(dataUrl, selectionBounds) {
return new Promise((resolve, reject) => {
fabric.Image.fromURL(
dataUrl,
(img) => {
if (!img) {
reject(new Error("无法从DataURL创建图像"));
return;
}
// 计算画布中心位置
const canvasCenter = this.canvas.getCenter();
// 如果有选区边界信息,使用选区的原始位置和尺寸
let targetLeft = canvasCenter.left;
let targetTop = canvasCenter.top;
let targetWidth = img.width;
let targetHeight = img.height;
if (selectionBounds) {
// 使用选区的原始位置
targetLeft = selectionBounds.left + selectionBounds.width / 2;
targetTop = selectionBounds.top + selectionBounds.height / 2;
// 确保图像显示尺寸与选区尺寸一致
targetWidth = selectionBounds.width;
targetHeight = selectionBounds.height;
console.log(
`设置图像位置: left=${targetLeft}, top=${targetTop}, 尺寸: ${targetWidth}x${targetHeight}`
);
}
// 计算缩放比例以匹配目标尺寸
const scaleX = targetWidth / img.width;
const scaleY = targetHeight / img.height;
// 设置图像属性
img.set({
left: targetLeft,
top: targetTop,
scaleX: scaleX,
scaleY: scaleY,
originX: "center",
originY: "center",
selectable: true,
evented: true,
hasControls: true,
hasBorders: true,
cornerStyle: "circle",
cornerColor: "#007aff",
cornerSize: 10,
transparentCorners: false,
borderColor: "#007aff",
borderScaleFactor: 2,
// 优化图像渲染质量
objectCaching: false, // 禁用缓存以确保最佳质量
statefullCache: true,
noScaleCache: false,
});
// 更新坐标
img.setCoords();
resolve(img);
},
{
crossOrigin: "anonymous",
// 确保图像以最高质量加载
quality: 1.0,
}
);
});
}
/**
* 克隆fabric对象
* @param {Object} obj fabric对象
* @returns {Object} 克隆的对象
* @private
*/
async _cloneObject(obj) {
return new Promise((resolve, reject) => {
if (!obj) {
reject(new Error("对象无效,无法克隆"));
return;
}
try {
obj.clone((cloned) => {
resolve(cloned);
});
} catch (error) {
reject(error);
}
});
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,578 @@
import { Command, FunctionCommand } from "./Command";
/**
* 液化命令基类
* 所有液化相关命令的基类
*/
export class LiquifyCommand extends Command {
/**
* 创建液化命令
* @param {Object} options 配置选项
* @param {Object} options.canvas Fabric.js画布实例
* @param {Object} options.layerManager 图层管理器实例
* @param {Object} options.liquifyManager 液化管理器实例
* @param {String} options.mode 液化模式
* @param {Object} options.params 液化参数
* @param {Object} options.targetObject 目标对象
* @param {ImageData} options.originalData 原始图像数据
* @param {ImageData} options.resultData 变形后图像数据
*/
constructor(options) {
super({
name: options.name || `液化操作: ${options.mode || "未知模式"}`,
description:
options.description ||
`使用${options.mode || "未知模式"}模式进行液化操作`,
});
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.liquifyManager = options.liquifyManager;
this.mode = options.mode;
this.params = options.params || {};
this.targetObject = options.targetObject;
this.targetLayerId = options.targetLayerId;
this.originalData = options.originalData; // 操作前的图像数据
this.resultData = options.resultData; // 操作后的图像数据
this.savedState = null;
}
/**
* 执行液化操作
* @returns {Promise} 执行结果
*/
async execute() {
if (!this.canvas || !this.targetObject) {
throw new Error("液化命令缺少必要的画布或目标对象");
}
if (!this.resultData) {
// 如果没有预先计算的结果数据,现场执行变形
this.resultData = await this.liquifyManager.applyLiquify(
this.targetObject,
this.mode,
this.params
);
}
// 保存执行前的状态
this.savedState = await this._saveObjectState();
// 更新画布上的对象
await this._updateObjectWithResult();
// 刷新Canvas
this.canvas.renderAll();
return this.resultData;
}
/**
* 撤销液化操作
* @returns {Promise} 撤销结果
*/
async undo() {
if (!this.canvas || !this.targetObject || !this.savedState) {
throw new Error("无法撤销:缺少必要的状态信息");
}
// 恢复对象到原始状态
await this._restoreObjectState();
// 刷新Canvas
this.canvas.renderAll();
return true;
}
/**
* 保存对象状态
* @private
*/
async _saveObjectState() {
if (!this.targetObject) return null;
// 对于图像对象我们需要保存src和元数据
const state = {
src: this.targetObject.getSrc ? this.targetObject.getSrc() : null,
element: this.targetObject._element
? this.targetObject._element.cloneNode(true)
: null,
filters: this.targetObject.filters ? [...this.targetObject.filters] : [],
originalData: this.originalData,
targetLayerId: this.targetLayerId,
};
return state;
}
/**
* 恢复对象状态
* @private
*/
async _restoreObjectState() {
if (!this.targetObject || !this.savedState) return false;
// 获取当前图层对象
const layer = this.layerManager.getLayerById(this.savedState.targetLayerId);
if (!layer) return false;
// 恢复原始图像
if (this.savedState.element && this.targetObject.setElement) {
this.targetObject.setElement(this.savedState.element);
// 恢复滤镜
if (this.savedState.filters) {
this.targetObject.filters = [...this.savedState.filters];
this.targetObject.applyFilters();
}
}
return true;
}
/**
* 使用变形结果更新对象
* @private
*/
async _updateObjectWithResult() {
if (!this.targetObject || !this.resultData) return false;
// 创建临时canvas来渲染结果数据
const tempCanvas = document.createElement("canvas");
tempCanvas.width = this.resultData.width;
tempCanvas.height = this.resultData.height;
const tempCtx = tempCanvas.getContext("2d");
tempCtx.putImageData(this.resultData, 0, 0);
console.log("临时Canvas创建成功 _updateObjectWithResult", this.resultData);
// 更新Fabric图像
await new Promise((resolve) => {
fabric.Image.fromURL(tempCanvas.toDataURL(), (img) => {
// 保留原对象的属性
img.set({
left: this.targetObject.left,
top: this.targetObject.top,
scaleX: this.targetObject.scaleX,
scaleY: this.targetObject.scaleY,
angle: this.targetObject.angle,
flipX: this.targetObject.flipX,
flipY: this.targetObject.flipY,
opacity: this.targetObject.opacity,
});
// 替换Canvas上的对象
const index = this.canvas.getObjects().indexOf(this.targetObject);
if (index !== -1) {
this.canvas.remove(this.targetObject);
this.canvas.insertAt(img, index);
this.targetObject = img;
}
// 确保图层引用更新
const layer = this.layerManager.getLayerById(this.targetLayerId);
if (layer) {
if (
layer.type === "background" &&
layer.fabricObject === this.targetObject
) {
layer.fabricObject = img;
} else if (layer.fabricObjects) {
const objIndex = layer.fabricObjects.indexOf(this.targetObject);
if (objIndex !== -1) {
layer.fabricObjects[objIndex] = img;
}
}
}
resolve();
});
});
return true;
}
}
/**
* 图层栅格化命令
* 用于将复杂图层栅格化为单一图像,以便进行液化操作
*/
export class RasterizeForLiquifyCommand extends Command {
/**
* 创建栅格化命令
* @param {Object} options 配置选项
* @param {Object} options.canvas Fabric.js画布实例
* @param {Object} options.layerManager 图层管理器实例
* @param {String} options.layerId 需要栅格化的图层ID
*/
constructor(options) {
super({
name: options.name || "栅格化图层",
description: options.description || "将图层栅格化为单一图像以便液化操作",
});
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.layerId = options.layerId;
this.originalLayer = null;
this.rasterizedImageObj = null;
}
/**
* 执行栅格化操作
* @returns {Promise<Object>} 栅格化后的图像对象
*/
async execute() {
if (!this.canvas || !this.layerManager || !this.layerId) {
throw new Error("栅格化命令缺少必要参数");
}
// 保存原始图层信息
this.originalLayer = this.layerManager.getLayerById(this.layerId);
if (!this.originalLayer) {
throw new Error(`图层ID不存在: ${this.layerId}`);
}
// 栅格化图层
const rasterizedImage = await this.layerManager.rasterizeLayer(
this.layerId
);
if (!rasterizedImage) {
throw new Error("栅格化图层失败");
}
this.rasterizedImageObj = rasterizedImage;
return rasterizedImage;
}
/**
* 撤销栅格化操作
* 注意:完整撤销栅格化是复杂的,这里提供近似还原
* @returns {Promise<boolean>} 撤销结果
*/
async undo() {
if (!this.canvas || !this.layerManager || !this.originalLayer) {
throw new Error("无法撤销:缺少必要的状态信息");
}
// 恢复图层为原始状态是复杂的这里可能需要与LayerManager协作
// 这个实现可能需要根据实际的LayerManager功能来调整
const restored = await this.layerManager.restoreLayerFromBackup(
this.layerId
);
if (!restored) {
console.warn("无法完全还原栅格化前的图层状态");
}
return true;
}
}
/**
* 液化工具初始化命令
* 用于初始化液化工具的状态,不执行实际操作
*/
export class InitLiquifyToolCommand extends Command {
constructor(options) {
super({
name: "初始化液化工具",
description: "准备液化工具工作环境",
undoable: false, // 这个命令不需要撤销
});
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.liquifyManager = options.liquifyManager;
this.toolManager = options.toolManager;
}
/**
* 执行初始化
*/
execute() {
if (this.liquifyManager) {
this.liquifyManager.initialize({
canvas: this.canvas,
layerManager: this.layerManager,
});
}
// 通知各管理器进入液化模式
this.toolManager?.notifyObservers("LIQUIFY");
return true;
}
undo() {
// 不需要撤销初始化
return true;
}
}
/**
* 液化操作命令 - 针对单次变形操作
* 用于实现可撤销的单次液化变形
*/
export class LiquifyDeformCommand extends LiquifyCommand {
/**
* 创建液化变形命令
* @param {Object} options 配置选项
* @param {Number} options.x 变形中心X坐标
* @param {Number} options.y 变形中心Y坐标
* @param {ImageData} options.beforeData 变形前的图像数据
* @param {ImageData} options.afterData 变形后的图像数据
*/
constructor(options) {
super({
...options,
name: `液化变形: ${options.mode || "未知模式"}`,
description: `在(${options.x}, ${options.y})应用${
options.mode || "未知模式"
}变形`,
});
this.x = options.x;
this.y = options.y;
this.beforeData = options.beforeData;
this.afterData = options.afterData;
}
async execute() {
if (!this.afterData) {
// 如果没有预计算的结果,实时计算
await this.liquifyManager.prepareForLiquify(this.targetObject);
this.afterData = await this.liquifyManager.applyLiquify(
this.targetObject,
this.mode,
this.params,
this.x,
this.y
);
}
// 保存当前状态
this.savedState = await this._saveObjectState();
// 应用变形结果
await this._updateObjectWithImageData(this.afterData);
this.canvas.renderAll();
return this.afterData;
}
async undo() {
if (!this.beforeData) {
throw new Error("无法撤销:缺少变形前的数据");
}
// 恢复到变形前的状态
await this._updateObjectWithImageData(this.beforeData);
this.canvas.renderAll();
return true;
}
/**
* 使用图像数据更新对象
* @param {ImageData} imageData 图像数据
* @private
*/
async _updateObjectWithImageData(imageData) {
return new Promise((resolve) => {
// 创建临时canvas
const tempCanvas = document.createElement("canvas");
tempCanvas.width = imageData.width;
tempCanvas.height = imageData.height;
const tempCtx = tempCanvas.getContext("2d");
tempCtx.putImageData(imageData, 0, 0);
// 从canvas创建新的fabric图像
fabric.Image.fromURL(tempCanvas.toDataURL(), (img) => {
// 保留原对象的变换属性
img.set({
left: this.targetObject.left,
top: this.targetObject.top,
scaleX: this.targetObject.scaleX,
scaleY: this.targetObject.scaleY,
angle: this.targetObject.angle,
flipX: this.targetObject.flipX,
flipY: this.targetObject.flipY,
opacity: this.targetObject.opacity,
});
// 替换canvas上的对象
const index = this.canvas.getObjects().indexOf(this.targetObject);
if (index !== -1) {
this.canvas.remove(this.targetObject);
this.canvas.insertAt(img, index);
this.targetObject = img;
// 更新图层引用
const layer = this.layerManager.getLayerById(this.targetLayerId);
if (layer) {
if (
layer.type === "background" &&
(layer.fabricObject === this.savedState?.originalObject ||
layer.fabricObject === this.targetObject)
) {
layer.fabricObject = img;
} else if (layer.fabricObjects) {
const objIndex = layer.fabricObjects.findIndex(
(obj) =>
obj === this.savedState?.originalObject ||
obj === this.targetObject
);
if (objIndex !== -1) {
layer.fabricObjects[objIndex] = img;
}
}
}
}
resolve();
});
});
}
}
/**
* 复合液化命令 - 用于组合多个液化操作
* 支持一次撤销多个相关的液化变形
*/
export class CompositeLiquifyCommand extends Command {
/**
* 创建复合液化命令
* @param {Object} options 配置选项
* @param {Array} options.commands 子命令列表
* @param {String} options.name 命令名称
*/
constructor(options) {
super({
name: options.name || "液化操作组合",
description:
options.description || `包含${options.commands?.length || 0}个液化操作`,
});
this.commands = options.commands || [];
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.liquifyManager = options.liquifyManager;
}
/**
* 添加子命令
* @param {Command} command 要添加的命令
*/
addCommand(command) {
this.commands.push(command);
}
async execute() {
const results = [];
for (const command of this.commands) {
try {
const result = await command.execute();
results.push(result);
} catch (error) {
// 如果有命令失败,尝试回滚已执行的命令
console.error("复合液化命令执行失败:", error);
await this.undo();
throw error;
}
}
return results;
}
async undo() {
// 逆序撤销所有子命令
const errors = [];
for (let i = this.commands.length - 1; i >= 0; i--) {
try {
await this.commands[i].undo();
} catch (error) {
console.error(`撤销子命令${i}失败:`, error);
errors.push(error);
}
}
if (errors.length > 0) {
throw new Error(`复合命令撤销部分失败: ${errors.length}个错误`);
}
return true;
}
/**
* 检查命令是否可以执行
* @returns {Boolean} 是否可执行
*/
canExecute() {
return (
this.commands.length > 0 &&
this.commands.every((cmd) => (cmd.canExecute ? cmd.canExecute() : true))
);
}
}
/**
* 液化重置命令 - 将图像恢复到原始状态
*/
export class LiquifyResetCommand extends LiquifyCommand {
constructor(options) {
super({
...options,
name: "重置液化",
description: "将图像恢复到液化前的原始状态",
});
}
async execute() {
if (!this.liquifyManager || !this.targetObject) {
throw new Error("无法重置:缺少必要的管理器或目标对象");
}
// 保存当前状态
this.savedState = await this._saveObjectState();
// 重置液化管理器
const resetData = this.liquifyManager.reset();
if (!resetData) {
throw new Error("重置失败:没有原始数据");
}
// 应用重置结果
await this._updateObjectWithResult();
this.canvas.renderAll();
return resetData;
}
}
/**
* 辅助函数:创建液化重置命令
* @param {Object} options 配置选项
* @returns {LiquifyResetCommand} 重置命令实例
*/
export function createLiquifyResetCommand(options) {
return new LiquifyResetCommand(options);
}
/**
* 辅助函数:创建液化变形命令
* @param {Object} options 配置选项
* @returns {LiquifyDeformCommand} 变形命令实例
*/
export function createLiquifyDeformCommand(options) {
return new LiquifyDeformCommand(options);
}
/**
* 辅助函数:创建复合液化命令
* @param {Object} options 配置选项
* @returns {CompositeLiquifyCommand} 复合命令实例
*/
export function createCompositeLiquifyCommand(options) {
return new CompositeLiquifyCommand(options);
}

View File

@@ -0,0 +1,103 @@
/**
* 查询类命令示例 - 不需要撤销
*/
export class GetCanvasInfoCommand {
constructor(options) {
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.undoable = false; // 明确标记为不可撤销
}
execute() {
return {
width: this.canvas.getWidth(),
height: this.canvas.getHeight(),
zoom: this.canvas.getZoom(),
layers: this.layerManager?.getLayers()?.length || 0,
objects: this.canvas.getObjects().length,
};
}
// 查询类命令不需要实现 undo 方法
}
/**
* 导出类命令示例 - 不需要撤销
*/
export class ExportCanvasCommand {
constructor(options) {
this.canvas = options.canvas;
this.format = options.format || "png";
this.quality = options.quality || 1;
this.undoable = false;
}
execute() {
return this.canvas.toDataURL(`image/${this.format}`, this.quality);
}
}
/**
* 验证类命令示例 - 不需要撤销
*/
export class ValidateCanvasCommand {
constructor(options) {
this.canvas = options.canvas;
this.undoable = false;
}
execute() {
const objects = this.canvas.getObjects();
const errors = [];
objects.forEach((obj, index) => {
if (!obj.left || !obj.top) {
errors.push(`对象 ${index} 位置无效`);
}
if (obj.width <= 0 || obj.height <= 0) {
errors.push(`对象 ${index} 尺寸无效`);
}
});
return {
isValid: errors.length === 0,
errors,
objectCount: objects.length,
};
}
}
/**
* 系统清理命令示例 - 不可逆操作,不需要撤销
*/
export class CleanupTempDataCommand {
constructor(options) {
this.canvas = options.canvas;
this.undoable = false;
}
execute() {
// 清理临时数据
const cleaned = [];
// 移除无效对象
const objects = this.canvas.getObjects();
objects.forEach((obj, index) => {
if (obj._isTemp || obj._invalid) {
this.canvas.remove(obj);
cleaned.push(`临时对象 ${index}`);
}
});
// 清理缓存
if (this.canvas._clearCache) {
this.canvas._clearCache();
cleaned.push("画布缓存");
}
return {
cleaned,
count: cleaned.length,
};
}
}

View File

@@ -0,0 +1,942 @@
import { OperationType } from "../utils/layerHelper";
import { Command } from "./Command";
import { generateId } from "../utils/helper";
/**
* 设置活动图层命令
*/
export class SetActiveLayerCommand extends Command {
constructor(options) {
super({
name: "设置活动图层",
saveState: false,
});
this.layers = options.layers;
this.canvas = options.canvas;
this.activeLayerId = options.activeLayerId;
this.layerId = options.layerId;
this.oldActiveLayerId = this.activeLayerId.value;
this.layerManager = options.layerManager;
this.oldActiveObjects = [];
this.newLayer = null;
this.editorMode = options.editorMode;
}
execute() {
this.newLayer = this.layers.value.find(
(layer) => layer.id === this.layerId
);
if (!this.newLayer) {
console.error(`图层 ${this.layerId} 不存在`);
return false;
}
// 如果是背景层,不设置为活动图层
if (this.newLayer.isBackground) {
console.warn("背景层不能设为活动图层");
return false;
}
// 如果图层已锁定,不设置为活动图层
if (this.newLayer.locked) {
console.warn("锁定图层不能设为活动图层");
return false;
}
this.oldActiveObjects = this.canvas.getActiveObjects();
// 设置为活动图层
this.activeLayerId.value = this.layerId;
// 如果在选择模式下,取消所有选择
if (this.editorMode === OperationType.SELECT && this.canvas) {
this.canvas.discardActiveObject();
// 设置为新的图层下的对象为激活,但需要确保对象存在于画布上
if (
this.newLayer.fabricObjects &&
this.newLayer.fabricObjects.length > 0
) {
const canvasObjects = this.canvas.getObjects();
const validObjects = this.newLayer.fabricObjects.filter(
(obj) => obj && canvasObjects.includes(obj)
);
if (validObjects.length > 0) {
if (validObjects.length === 1) {
// 只有一个对象时直接设置
this.canvas.setActiveObject(validObjects[0]);
} else {
// 多个对象时创建活动选择组
const activeSelection = new fabric.ActiveSelection(validObjects, {
canvas: this.canvas,
});
this.canvas.setActiveObject(activeSelection);
}
}
}
this.canvas.renderAll();
}
return true;
}
undo() {
// 恢复原活动图层ID
this.activeLayerId.value = this.oldActiveLayerId;
// 如果在选择模式下,恢复取消的所有选择
if (this.editorMode === OperationType.SELECT && this.canvas) {
this.canvas.discardActiveObject();
// 修复:确保对象存在于画布上才激活
if (this.oldActiveObjects && this.oldActiveObjects.length > 0) {
const canvasObjects = this.canvas.getObjects();
const validObjects = this.oldActiveObjects.filter(
(obj) => obj && canvasObjects.includes(obj)
);
if (validObjects.length > 0) {
if (validObjects.length > 1) {
// 如果有多个对象,需要创建一个活动选择组
const activeSelection = new fabric.ActiveSelection(validObjects, {
canvas: this.canvas,
});
this.canvas.setActiveObject(activeSelection);
} else {
// 只有一个对象时直接设置
this.canvas.setActiveObject(validObjects[0]);
}
}
}
this.canvas.renderAll();
}
}
getInfo() {
return {
name: this.name,
layerId: this.layerId,
oldActiveLayerId: this.oldActiveLayerId,
};
}
}
/**
* 添加对象到图层命令
*/
export class AddObjectToLayerCommand extends Command {
constructor(options) {
super({
name: "添加对象到图层",
saveState: true,
});
this.canvas = options.canvas;
this.layers = options.layers;
this.layerId = options.layerId;
this.fabricObject = options.fabricObject;
// 保存对象原始状态和ID
this.objectId =
this.fabricObject.id ||
`obj_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
this.originalObjectState = this.fabricObject.toObject([
"id",
"layerId",
"layerName",
]);
}
execute() {
// 查找目标图层
const layer = this.layers.value.find((l) => l.id === this.layerId);
if (!layer) {
console.error(`图层 ${this.layerId} 不存在`);
return null;
}
// 如果是背景层,不允许添加对象
if (layer.isBackground) {
console.warn("不能向背景层添加对象");
return null;
}
// 为对象生成唯一ID
this.fabricObject.id = this.objectId;
// 设置对象与图层的关联
this.fabricObject.layerId = this.layerId;
this.fabricObject.layerName = layer.name;
// 设置对象可操作可选择
this.fabricObject.selectable = true;
this.fabricObject.evented = true;
// 将对象添加到画布
this.canvas.add(this.fabricObject);
// 将对象添加到图层的fabricObjects数组
layer.fabricObjects = layer.fabricObjects || [];
layer.fabricObjects.push(this.fabricObject);
this.canvas.discardActiveObject();
// 确保对象确实存在于画布上才激活
const canvasObjects = this.canvas.getObjects();
const validObjects = layer.fabricObjects.filter(
(obj) => obj && canvasObjects.includes(obj)
);
if (validObjects.length > 0) {
if (validObjects.length === 1) {
// 只有一个对象时直接设置
this.canvas.setActiveObject(validObjects[0]);
} else {
// 多个对象时创建活动选择组
const activeSelection = new fabric.ActiveSelection(validObjects, {
canvas: this.canvas,
});
this.canvas.setActiveObject(activeSelection);
}
}
// 更新画布
this.canvas.renderAll();
return this.fabricObject;
}
undo() {
// 查找图层
const layer = this.layers.value.find((l) => l.id === this.layerId);
if (!layer) {
return false;
}
// 从图层的fabricObjects数组中移除对象
if (layer.fabricObjects) {
layer.fabricObjects = layer.fabricObjects.filter(
(obj) => obj.id !== this.objectId
);
}
// 从画布移除对象
const object = this.canvas
.getObjects()
.find((obj) => obj.id === this.objectId);
if (object) {
// 先丢弃活动对象,避免控制点渲染错误
this.canvas.discardActiveObject();
this.canvas.remove(object);
// 更新画布
this.canvas.renderAll();
}
return true;
}
getInfo() {
return {
name: this.name,
layerId: this.layerId,
objectId: this.objectId,
};
}
}
/**
* 从图层中移除对象命令
*/
export class RemoveObjectFromLayerCommand extends Command {
constructor(options) {
super({
name: "从图层中移除对象",
saveState: true,
});
this.canvas = options.canvas;
this.layers = options.layers;
this.objectId = options.objectId;
// 查找对象和图层
this.object =
typeof options.objectOrId === "object"
? options.objectOrId
: this.canvas.getObjects().find((obj) => obj.id === this.objectId);
if (this.object) {
this.layerId = this.object.layerId;
this.objectData = this.object.toObject(["id", "layerId", "layerName"]);
}
}
execute() {
if (!this.object) {
console.error(`对象 ${this.objectId} 不存在`);
return false;
}
if (!this.layerId) {
console.error(`对象 ${this.objectId} 未关联到任何图层`);
return false;
}
// 查找图层
const layer = this.layers.value.find((l) => l.id === this.layerId);
if (!layer) {
console.error(`图层 ${this.layerId} 不存在`);
return false;
}
// 从画布移除对象
this.canvas.remove(this.object);
// 从图层的fabricObjects数组移除对象
if (layer.fabricObjects) {
layer.fabricObjects = layer.fabricObjects.filter(
(obj) => obj.id !== this.objectId
);
}
// 更新画布
this.canvas.renderAll();
return true;
}
undo() {
if (!this.objectData || !this.layerId) {
return false;
}
// 查找图层
const layer = this.layers.value.find((l) => l.id === this.layerId);
if (!layer) {
return false;
}
// 恢复对象到画布
fabric.util.enlivenObjects([this.objectData], (objects) => {
const restoredObject = objects[0];
// 将对象添加到画布
this.canvas.add(restoredObject);
// 将对象添加回图层
layer.fabricObjects = layer.fabricObjects || [];
layer.fabricObjects.push(restoredObject);
// 更新画布
this.canvas.renderAll();
});
return true;
}
getInfo() {
return {
name: this.name,
objectId: this.objectId,
layerId: this.layerId,
};
}
}
/**
* 更换固定图层图像命令
* 专门用于更换固定图层(如背景图层)的图像
*/
export class ChangeFixedImageCommand extends Command {
constructor(options = {}) {
super();
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.imageUrl = options.imageUrl;
this.targetLayerType = options.targetLayerType || "background"; // 'background', 'fixed', etc.
this.position = options.position || { x: 0, y: 0 };
this.scale = options.scale || { x: 1, y: 1 };
this.preserveTransform = options.preserveTransform !== false; // 默认保留变换
// 用于回滚的状态
this.previousImage = null;
this.previousTransform = null;
this.targetLayer = null;
this.isExecuted = false;
// 错误处理
this.maxRetries = options.maxRetries || 3;
this.retryCount = 0;
this.timeoutMs = options.timeoutMs || 10000;
}
async execute() {
try {
this.validateInputs();
// 查找目标图层
this.targetLayer = this.findTargetLayer();
if (!this.targetLayer) {
throw new Error(`找不到目标图层类型: ${this.targetLayerType}`);
}
// 保存当前状态用于回滚
await this.saveCurrentState();
// 加载新图像
const newImage = await this.loadImageWithRetry();
// 应用图像到图层
await this.applyImageToLayer(newImage);
this.isExecuted = true;
// 触发成功事件
this.emitEvent("image:changed", {
layerId: this.targetLayer.id,
newImageUrl: this.imageUrl,
command: this,
});
return {
success: true,
layerId: this.targetLayer.id,
imageUrl: this.imageUrl,
};
} catch (error) {
console.error("ChangeFixedImageCommand执行失败:", error);
// 如果已经执行了部分操作,尝试回滚
if (this.isExecuted) {
try {
await this.undo();
} catch (rollbackError) {
console.error("回滚失败:", rollbackError);
}
}
throw error;
}
}
async undo() {
if (!this.isExecuted || !this.targetLayer) {
throw new Error("命令未执行或目标图层不存在");
}
try {
if (this.previousImage) {
// 恢复之前的图像
await this.restorePreviousImage();
} else {
// 如果没有之前的图像,移除当前图像
await this.removeCurrentImage();
}
this.isExecuted = false;
// 触发撤销事件
this.emitEvent("image:reverted", {
layerId: this.targetLayer.id,
command: this,
});
return {
success: true,
action: "reverted",
layerId: this.targetLayer.id,
};
} catch (error) {
console.error("ChangeFixedImageCommand撤销失败:", error);
throw error;
}
}
validateInputs() {
if (!this.canvas) throw new Error("Canvas实例是必需的");
if (!this.layerManager) throw new Error("LayerManager实例是必需的");
if (!this.imageUrl) throw new Error("图像URL是必需的");
// 验证URL格式
try {
new URL(this.imageUrl);
} catch {
throw new Error("无效的图像URL格式");
}
}
findTargetLayer() {
const layers = this.layerManager.layers?.value || [];
switch (this.targetLayerType) {
case "background":
return layers.find((layer) => layer.isBackground);
case "fixed":
return layers.find((layer) => layer.isFixed);
default:
return layers.find((layer) => layer.type === this.targetLayerType);
}
}
async saveCurrentState() {
if (!this.targetLayer.fabricObject) return;
const currentObj = this.targetLayer.fabricObject;
// 保存当前图像URL如果存在
this.previousImage = {
url: currentObj.getSrc ? currentObj.getSrc() : null,
element: currentObj._element ? currentObj._element.cloneNode() : null,
};
// 保存变换状态
this.previousTransform = {
left: currentObj.left,
top: currentObj.top,
scaleX: currentObj.scaleX,
scaleY: currentObj.scaleY,
angle: currentObj.angle,
flipX: currentObj.flipX,
flipY: currentObj.flipY,
opacity: currentObj.opacity,
};
}
async loadImageWithRetry() {
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
try {
return await this.loadImage();
} catch (error) {
this.retryCount = attempt;
if (attempt === this.maxRetries) {
throw new Error(
`图像加载失败,已重试${this.maxRetries}次: ${error.message}`
);
}
// 指数退避重试
const delay = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, delay));
console.warn(
`图像加载重试 ${attempt + 1}/${this.maxRetries}:`,
error.message
);
}
}
}
loadImage() {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(
new Error(`图像加载超时 (${this.timeoutMs}ms): ${this.imageUrl}`)
);
}, this.timeoutMs);
fabric.Image.fromURL(
this.imageUrl,
(img) => {
clearTimeout(timeout);
if (!img || !img.getElement()) {
reject(new Error("图像加载失败或无效"));
return;
}
resolve(img);
},
{
crossOrigin: "anonymous",
}
);
});
}
async applyImageToLayer(newImage) {
const currentObj = this.targetLayer.fabricObject;
// 设置基本属性
newImage.set({
id: currentObj?.id || generateId(),
layerId: this.targetLayer.id,
layerName: this.targetLayer.name,
isBackground: this.targetLayer.isBackground,
isFixed: this.targetLayer.isFixed,
});
// 应用位置和变换
if (this.preserveTransform && this.previousTransform) {
newImage.set(this.previousTransform);
} else {
newImage.set({
left: this.position.x,
top: this.position.y,
scaleX: this.scale.x,
scaleY: this.scale.y,
});
}
// 移除旧对象(如果存在)
if (currentObj) {
this.canvas.remove(currentObj);
}
// 添加新图像
this.canvas.add(newImage);
newImage.setCoords();
// 更新图层引用
this.targetLayer.fabricObject = newImage;
// 更新图层管理器
this.layerManager.updateLayerObject(this.targetLayer.id, newImage);
// 重新渲染画布
this.canvas.renderAll();
}
async restorePreviousImage() {
if (!this.previousImage.url) return;
const restoredImage = await this.loadImageFromUrl(this.previousImage.url);
// 恢复之前的变换
if (this.previousTransform) {
restoredImage.set(this.previousTransform);
}
// 设置图层属性
restoredImage.set({
id: this.targetLayer.fabricObject?.id || generateId(),
layerId: this.targetLayer.id,
layerName: this.targetLayer.name,
isBackground: this.targetLayer.isBackground,
isFixed: this.targetLayer.isFixed,
});
// 替换当前对象
if (this.targetLayer.fabricObject) {
this.canvas.remove(this.targetLayer.fabricObject);
}
this.canvas.add(restoredImage);
restoredImage.setCoords();
// 更新引用
this.targetLayer.fabricObject = restoredImage;
this.layerManager.updateLayerObject(this.targetLayer.id, restoredImage);
this.canvas.renderAll();
}
async removeCurrentImage() {
if (this.targetLayer.fabricObject) {
this.canvas.remove(this.targetLayer.fabricObject);
this.targetLayer.fabricObject = null;
this.layerManager.updateLayerObject(this.targetLayer.id, null);
this.canvas.renderAll();
}
}
loadImageFromUrl(url) {
return new Promise((resolve, reject) => {
fabric.Image.fromURL(
url,
(img) => {
if (!img || !img.getElement()) {
reject(new Error("恢复图像加载失败"));
return;
}
resolve(img);
},
{ crossOrigin: "anonymous" }
);
});
}
emitEvent(eventName, data) {
if (this.canvas && this.canvas.fire) {
this.canvas.fire(eventName, data);
}
}
// 获取命令信息用于调试
getCommandInfo() {
return {
type: "ChangeFixedImageCommand",
targetLayerType: this.targetLayerType,
imageUrl: this.imageUrl,
isExecuted: this.isExecuted,
retryCount: this.retryCount,
targetLayerId: this.targetLayer?.id,
preserveTransform: this.preserveTransform,
};
}
}
/**
* 向图层添加图像命令
* 用于向指定图层添加新的图像对象
*/
export class AddImageToLayerCommand extends Command {
constructor(options = {}) {
super();
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.imageUrl = options.imageUrl;
this.layerId = options.layerId;
this.position = options.position || { x: 100, y: 100 };
this.scale = options.scale || { x: 1, y: 1 };
this.zIndex = options.zIndex || null; // 可选的层级控制
// 用于回滚的状态
this.addedObject = null;
this.targetLayer = null;
this.isExecuted = false;
// 错误处理
this.maxRetries = options.maxRetries || 3;
this.retryCount = 0;
this.timeoutMs = options.timeoutMs || 10000;
}
async execute() {
try {
this.validateInputs();
// 查找目标图层
this.targetLayer = this.findTargetLayer();
if (!this.targetLayer) {
throw new Error(`找不到目标图层: ${this.layerId}`);
}
// 检查图层是否可编辑
this.validateLayerEditability();
// 加载新图像
const newImage = await this.loadImageWithRetry();
// 添加图像到图层
await this.addImageToLayer(newImage);
this.isExecuted = true;
// 触发成功事件
this.emitEvent("image:added", {
layerId: this.layerId,
objectId: this.addedObject.id,
imageUrl: this.imageUrl,
command: this,
});
return {
success: true,
layerId: this.layerId,
objectId: this.addedObject.id,
imageUrl: this.imageUrl,
};
} catch (error) {
console.error("AddImageToLayerCommand执行失败:", error);
// 如果已经添加了对象,尝试移除
if (this.addedObject) {
try {
await this.undo();
} catch (rollbackError) {
console.error("回滚失败:", rollbackError);
}
}
throw error;
}
}
async undo() {
if (!this.isExecuted || !this.addedObject) {
throw new Error("命令未执行或没有添加的对象");
}
try {
// 移除添加的对象
this.canvas.remove(this.addedObject);
// 从图层管理器中移除
this.layerManager.removeObjectFromLayer(
this.addedObject.id,
this.layerId
);
this.isExecuted = false;
// 触发撤销事件
this.emitEvent("image:removed", {
layerId: this.layerId,
objectId: this.addedObject.id,
command: this,
});
// 重新渲染
this.canvas.renderAll();
return {
success: true,
action: "removed",
layerId: this.layerId,
objectId: this.addedObject.id,
};
} catch (error) {
console.error("AddImageToLayerCommand撤销失败:", error);
throw error;
}
}
validateInputs() {
if (!this.canvas) throw new Error("Canvas实例是必需的");
if (!this.layerManager) throw new Error("LayerManager实例是必需的");
if (!this.imageUrl) throw new Error("图像URL是必需的");
if (!this.layerId) throw new Error("图层ID是必需的");
// 验证URL格式
try {
new URL(this.imageUrl);
} catch {
throw new Error("无效的图像URL格式");
}
}
findTargetLayer() {
const layers = this.layerManager.layers?.value || [];
return layers.find((layer) => layer.id === this.layerId);
}
validateLayerEditability() {
if (this.targetLayer.locked) {
throw new Error("目标图层已锁定,无法添加对象");
}
if (!this.targetLayer.visible) {
console.warn("目标图层不可见,添加的对象可能不会显示");
}
}
async loadImageWithRetry() {
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
try {
return await this.loadImage();
} catch (error) {
this.retryCount = attempt;
if (attempt === this.maxRetries) {
throw new Error(
`图像加载失败,已重试${this.maxRetries}次: ${error.message}`
);
}
// 指数退避重试
const delay = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, delay));
console.warn(
`图像加载重试 ${attempt + 1}/${this.maxRetries}:`,
error.message
);
}
}
}
loadImage() {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(
new Error(`图像加载超时 (${this.timeoutMs}ms): ${this.imageUrl}`)
);
}, this.timeoutMs);
fabric.Image.fromURL(
this.imageUrl,
(img) => {
clearTimeout(timeout);
if (!img || !img.getElement()) {
reject(new Error("图像加载失败或无效"));
return;
}
resolve(img);
},
{
crossOrigin: "anonymous",
}
);
});
}
async addImageToLayer(newImage) {
// 生成唯一ID
const objectId = generateId();
// 设置图像属性
newImage.set({
id: objectId,
layerId: this.layerId,
layerName: this.targetLayer.name,
left: this.position.x,
top: this.position.y,
scaleX: this.scale.x,
scaleY: this.scale.y,
selectable: true,
evented: true,
});
// 添加到画布
this.canvas.add(newImage);
// 设置层级
if (this.zIndex !== null) {
this.setObjectZIndex(newImage, this.zIndex);
}
newImage.setCoords();
// 保存引用用于回滚
this.addedObject = newImage;
// 添加到图层管理器
this.layerManager.addObjectToLayer(newImage, this.layerId);
// 重新渲染画布
this.canvas.renderAll();
}
setObjectZIndex(object, zIndex) {
if (zIndex === "top") {
object.bringToFront();
} else if (zIndex === "bottom") {
object.sendToBack();
} else if (typeof zIndex === "number") {
object.moveTo(zIndex);
}
}
emitEvent(eventName, data) {
if (this.canvas && this.canvas.fire) {
this.canvas.fire(eventName, data);
}
}
// 获取命令信息用于调试
getCommandInfo() {
return {
type: "AddImageToLayerCommand",
layerId: this.layerId,
imageUrl: this.imageUrl,
position: this.position,
scale: this.scale,
isExecuted: this.isExecuted,
retryCount: this.retryCount,
addedObjectId: this.addedObject?.id,
};
}
}

View File

@@ -0,0 +1,379 @@
import { OperationType } from "../utils/layerHelper.js";
import { Command, CompositeCommand } from "./Command.js";
//import { fabric } from "fabric-with-all";
/**
* 批量初始化红绿图模式命令
* 将衣服底图添加到背景层、红绿图添加到固定图层、调整位置和大小,以及设置画布背景为白色等操作合并到一个命令中
* 减少页面闪烁,一次性渲染完成
*/
export class BatchInitializeRedGreenModeCommand extends Command {
constructor(options = {}) {
super({
name: "批量初始化红绿图模式",
description: "一次性完成红绿图模式的所有初始化操作",
});
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.toolManager = options.toolManager;
this.clothingImageUrl = options.clothingImageUrl;
this.redGreenImageUrl = options.redGreenImageUrl;
this.onImageGenerated = options.onImageGenerated;
this.normalLayerOpacity = options.normalLayerOpacity || 0.4;
// 存储原始状态以便撤销
this.originalCanvasBackground = null;
this.originalBackgroundObject = null;
this.originalFixedObjects = null;
this.originalNormalObjects = null;
this.originalNormalOpacities = new Map();
this.originalToolState = null;
// 存储加载的图片对象
this.clothingImage = null;
this.redGreenImage = null;
}
async execute() {
try {
// 禁用画布渲染以避免闪烁
this.canvas.renderOnAddRemove = false;
// 1. 设置画布背景为白色
this.originalCanvasBackground = this.canvas.backgroundColor;
this.canvas.setBackgroundColor('#ffffff', () => {});
// 2. 查找图层结构
const layers = this.layerManager.layers?.value || [];
const backgroundLayer = layers.find((layer) => layer.isBackground);
const fixedLayer = layers.find((layer) => layer.isFixed);
const normalLayers = layers.filter(
(layer) => !layer.isBackground && !layer.isFixed
);
if (!backgroundLayer || !fixedLayer || normalLayers.length === 0) {
throw new Error("缺少必要的图层结构");
}
const normalLayer = normalLayers[0]; // 使用第一个普通图层
// 3. 保存原始状态
this.originalBackgroundObject = backgroundLayer.fabricObject ? {
...backgroundLayer.fabricObject.toObject(),
ref: backgroundLayer.fabricObject
} : null;
this.originalFixedObjects = fixedLayer.fabricObject
? [fixedLayer.fabricObject]
: [];
this.originalNormalObjects = normalLayer.fabricObjects
? [...normalLayer.fabricObjects]
: [];
// 保存普通图层透明度
normalLayers.forEach((layer) => {
this.originalNormalOpacities.set(layer.id, layer.opacity || 1);
if (layer.fabricObjects) {
layer.fabricObjects.forEach((obj) => {
this.originalNormalOpacities.set(
`${layer.id}_${obj.id || "unknown"}`,
obj.opacity || 1
);
});
}
});
// 保存工具状态
if (this.toolManager) {
this.originalToolState = {
currentTool: this.toolManager.getCurrentTool(),
isRedGreenMode: this.toolManager.isRedGreenMode,
};
}
// 4. 确保背景图层大小正确
await this._setupBackgroundLayer(backgroundLayer);
// 5. 并行加载两个图片
const [clothingImg, redGreenImg] = await Promise.all([
this._loadImage(this.clothingImageUrl),
this._loadImage(this.redGreenImageUrl)
]);
// 6. 设置衣服底图到固定图层
await this._setupClothingImage(clothingImg, fixedLayer);
// 7. 设置红绿图到普通图层,位置和大小与衣服底图一致
await this._setupRedGreenImage(redGreenImg, normalLayer, this.clothingImage);
// 8. 设置普通图层透明度
this._setupNormalLayerOpacity(normalLayers);
// 9. 配置工具管理器
this._setupToolManager();
// 10. 重新启用渲染并执行一次性渲染
this.canvas.renderOnAddRemove = true;
this.canvas.renderAll();
console.log("批量红绿图模式初始化完成", {
衣服底图: this.clothingImageUrl,
红绿图: this.redGreenImageUrl,
普通图层透明度: `${Math.round(this.normalLayerOpacity * 100)}%`,
画布背景: "白色",
});
return true;
} catch (error) {
// 恢复渲染
this.canvas.renderOnAddRemove = true;
console.error("批量红绿图模式初始化失败:", error);
throw error;
}
}
async undo() {
try {
// 禁用渲染
this.canvas.renderOnAddRemove = false;
// 1. 恢复画布背景
if (this.originalCanvasBackground !== null) {
this.canvas.setBackgroundColor(this.originalCanvasBackground, () => {});
}
// 2. 恢复图层对象
const layers = this.layerManager.layers?.value || [];
const backgroundLayer = layers.find((layer) => layer.isBackground);
const fixedLayer = layers.find((layer) => layer.isFixed);
const normalLayers = layers.filter(
(layer) => !layer.isBackground && !layer.isFixed
);
// 移除当前添加的对象
if (this.clothingImage) {
this.canvas.remove(this.clothingImage);
}
if (this.redGreenImage) {
this.canvas.remove(this.redGreenImage);
}
// 恢复背景图层
if (backgroundLayer && this.originalBackgroundObject) {
if (this.originalBackgroundObject.ref) {
backgroundLayer.fabricObject = this.originalBackgroundObject.ref;
}
}
// 恢复固定图层
if (fixedLayer) {
fixedLayer.fabricObject = this.originalFixedObjects.length > 0
? this.originalFixedObjects[0]
: null;
if (fixedLayer.fabricObject) {
this.canvas.add(fixedLayer.fabricObject);
}
}
// 恢复普通图层
if (normalLayers.length > 0) {
const normalLayer = normalLayers[0];
normalLayer.fabricObjects = [...this.originalNormalObjects];
this.originalNormalObjects.forEach((obj) => {
this.canvas.add(obj);
});
}
// 3. 恢复透明度
normalLayers.forEach((layer) => {
if (this.originalNormalOpacities.has(layer.id)) {
layer.opacity = this.originalNormalOpacities.get(layer.id);
}
if (layer.fabricObjects) {
layer.fabricObjects.forEach((obj) => {
const key = `${layer.id}_${obj.id || "unknown"}`;
if (this.originalNormalOpacities.has(key)) {
obj.opacity = this.originalNormalOpacities.get(key);
}
});
}
});
// 4. 恢复工具状态
if (this.toolManager && this.originalToolState) {
this.toolManager.isRedGreenMode = this.originalToolState.isRedGreenMode;
if (this.originalToolState.currentTool) {
this.toolManager.setTool(this.originalToolState.currentTool);
}
}
// 5. 重新启用渲染
this.canvas.renderOnAddRemove = true;
this.canvas.renderAll();
return true;
} catch (error) {
this.canvas.renderOnAddRemove = true;
console.error("撤销批量红绿图模式初始化失败:", error);
return false;
}
}
/**
* 设置背景图层
*/
async _setupBackgroundLayer(backgroundLayer) {
let backgroundObject = backgroundLayer.fabricObject;
if (!backgroundObject) {
// 创建白色背景矩形
backgroundObject = new fabric.Rect({
left: 0,
top: 0,
width: this.canvas.width,
height: this.canvas.height,
fill: "#ffffff",
selectable: false,
evented: false,
isBackground: true,
layerId: backgroundLayer.id,
layerName: backgroundLayer.name,
});
this.canvas.add(backgroundObject);
this.canvas.sendToBack(backgroundObject);
backgroundLayer.fabricObject = backgroundObject;
} else {
// 更新现有背景对象大小
backgroundObject.set({
width: this.canvas.width,
height: this.canvas.height,
left: 0,
top: 0,
fill: "#ffffff", // 确保背景是白色
});
}
}
/**
* 加载图片
*/
async _loadImage(imageUrl) {
return new Promise((resolve, reject) => {
fabric.Image.fromURL(
imageUrl,
(img) => {
if (!img) {
reject(new Error(`无法加载图片: ${imageUrl}`));
return;
}
resolve(img);
},
{ crossOrigin: "anonymous" }
);
});
}
/**
* 设置衣服底图
*/
async _setupClothingImage(img, fixedLayer) {
// 计算图片缩放,保持上下留边距
const margin = 50;
const maxWidth = this.canvas.width - margin * 2;
const maxHeight = this.canvas.height - margin * 2;
const scale = Math.min(maxWidth / img.width, maxHeight / img.height);
img.set({
scaleX: scale,
scaleY: scale,
left: this.canvas.width / 2,
top: this.canvas.height / 2,
originX: "center",
originY: "center",
selectable: false,
evented: false,
layerId: fixedLayer.id,
layerName: fixedLayer.name,
});
// 清除固定图层原有内容
if (fixedLayer.fabricObject) {
this.canvas.remove(fixedLayer.fabricObject);
}
// 添加到画布和固定图层
this.canvas.add(img);
fixedLayer.fabricObject = img;
this.clothingImage = img;
}
/**
* 设置红绿图
*/
async _setupRedGreenImage(img, normalLayer, clothingImage) {
if (!clothingImage) {
throw new Error("衣服底图未加载,无法设置红绿图位置");
}
// 使用与衣服底图完全相同的属性
img.set({
scaleX: clothingImage.scaleX,
scaleY: clothingImage.scaleY,
left: clothingImage.left,
top: clothingImage.top,
originX: clothingImage.originX,
originY: clothingImage.originY,
selectable: false,
evented: false,
layerId: normalLayer.id,
layerName: normalLayer.name,
});
// 清除普通图层原有内容
if (normalLayer.fabricObjects) {
normalLayer.fabricObjects.forEach((obj) => {
this.canvas.remove(obj);
});
}
// 添加到画布和普通图层
this.canvas.add(img);
normalLayer.fabricObjects = [img];
this.redGreenImage = img;
}
/**
* 设置普通图层透明度
*/
_setupNormalLayerOpacity(normalLayers) {
normalLayers.forEach((layer) => {
// 设置图层透明度
layer.opacity = this.normalLayerOpacity;
// 更新图层中所有对象的透明度
if (layer.fabricObjects) {
layer.fabricObjects.forEach((obj) => {
obj.opacity = this.normalLayerOpacity;
});
}
});
}
/**
* 设置工具管理器
*/
_setupToolManager() {
if (this.toolManager) {
// 设置红绿图模式
this.toolManager.isRedGreenMode = true;
// 切换到红色笔刷工具
this.toolManager.setTool(OperationType.RED_BRUSH);
}
}
}

View File

@@ -0,0 +1,578 @@
import { Command, CompositeCommand } from "./Command.js";
//import { fabric } from "fabric-with-all";
import { createLayer, LayerType } from "../utils/layerHelper.js";
/**
* 创建选区命令
*/
export class CreateSelectionCommand extends Command {
constructor(options = {}) {
super({
name: options.name || "创建选区",
description: "在画布上创建选区",
saveState: false,
});
this.canvas = options.canvas;
this.selectionManager = options.selectionManager;
this.selectionObject = options.selectionObject;
this.selectionType = options.selectionType || "rectangle";
}
async execute() {
if (!this.canvas || !this.selectionManager || !this.selectionObject) {
console.error("无法创建选区:参数无效");
return false;
}
// 将选择对象添加到选区管理器
this.selectionManager.setSelectionObject(this.selectionObject);
return true;
}
async undo() {
if (!this.selectionManager) return false;
this.selectionManager.clearSelection();
return true;
}
}
/**
* 反转选区命令
*/
export class InvertSelectionCommand extends Command {
constructor(options = {}) {
super({
name: "反转选区",
description: "反转当前选区",
saveState: false,
});
this.canvas = options.canvas;
this.selectionManager = options.selectionManager;
this.originalSelection = options.selectionManager
? options.selectionManager.getSelectionPath()
: null;
}
async execute() {
if (!this.canvas || !this.selectionManager) {
console.error("无法反转选区:参数无效");
return false;
}
// 保存原始选区
if (!this.originalSelection) {
this.originalSelection = this.selectionManager.getSelectionPath();
}
// 反转选区
const result = await this.selectionManager.invertSelection();
return result;
}
async undo() {
if (!this.selectionManager || !this.originalSelection) return false;
// 恢复原始选区
this.selectionManager.setSelectionFromPath(this.originalSelection);
return true;
}
}
/**
* 添加到选区命令
*/
export class AddToSelectionCommand extends Command {
constructor(options = {}) {
super({
name: "添加到选区",
description: "将新的选区添加到现有选区",
saveState: false,
});
this.canvas = options.canvas;
this.selectionManager = options.selectionManager;
this.newSelection = options.newSelection;
this.originalSelection = options.selectionManager
? options.selectionManager.getSelectionPath()
: null;
}
async execute() {
if (!this.canvas || !this.selectionManager || !this.newSelection) {
console.error("无法添加到选区:参数无效");
return false;
}
// 保存原始选区
if (!this.originalSelection) {
this.originalSelection = this.selectionManager.getSelectionPath();
}
// 添加到选区
const result = await this.selectionManager.addToSelection(
this.newSelection
);
return result;
}
async undo() {
if (!this.selectionManager || !this.originalSelection) return false;
// 恢复原始选区
this.selectionManager.setSelectionFromPath(this.originalSelection);
return true;
}
}
/**
* 从选区中移除命令
*/
export class RemoveFromSelectionCommand extends Command {
constructor(options = {}) {
super({
name: "从选区中移除",
description: "从现有选区中移除指定区域",
saveState: false,
});
this.canvas = options.canvas;
this.selectionManager = options.selectionManager;
this.removeSelection = options.removeSelection;
this.originalSelection = options.selectionManager
? options.selectionManager.getSelectionPath()
: null;
}
async execute() {
if (!this.canvas || !this.selectionManager || !this.removeSelection) {
console.error("无法从选区中移除:参数无效");
return false;
}
// 保存原始选区
if (!this.originalSelection) {
this.originalSelection = this.selectionManager.getSelectionPath();
}
// 从选区中移除
const result = await this.selectionManager.removeFromSelection(
this.removeSelection
);
return result;
}
async undo() {
if (!this.selectionManager || !this.originalSelection) return false;
// 恢复原始选区
this.selectionManager.setSelectionFromPath(this.originalSelection);
return true;
}
}
/**
* 清除选区命令
*/
export class ClearSelectionCommand extends Command {
constructor(options = {}) {
super({
name: "清除选区",
description: "清除当前选区",
saveState: false,
});
this.selectionManager = options.selectionManager;
this.originalSelection = options.selectionManager
? options.selectionManager.getSelectionPath()
: null;
}
async execute() {
if (!this.selectionManager) {
console.error("无法清除选区:参数无效");
return false;
}
// 保存原始选区
if (!this.originalSelection) {
this.originalSelection = this.selectionManager.getSelectionPath();
}
// 清除选区
this.selectionManager.clearSelection();
return true;
}
async undo() {
if (!this.selectionManager || !this.originalSelection) return false;
// 恢复原始选区
this.selectionManager.setSelectionFromPath(this.originalSelection);
return true;
}
}
/**
* 羽化选区命令
*/
export class FeatherSelectionCommand extends Command {
constructor(options = {}) {
super({
name: "羽化选区",
description: "对当前选区应用羽化效果",
saveState: false,
});
this.selectionManager = options.selectionManager;
this.featherAmount = options.featherAmount || 5;
this.originalSelection = options.selectionManager
? options.selectionManager.getSelectionPath()
: null;
this.originalFeatherAmount = options.selectionManager
? options.selectionManager.getFeatherAmount()
: 0;
}
async execute() {
if (!this.selectionManager) {
console.error("无法羽化选区:参数无效");
return false;
}
// 保存原始选区和羽化值
if (!this.originalSelection) {
this.originalSelection = this.selectionManager.getSelectionPath();
this.originalFeatherAmount = this.selectionManager.getFeatherAmount();
}
// 应用羽化
const result = await this.selectionManager.featherSelection(
this.featherAmount
);
return result;
}
async undo() {
if (!this.selectionManager || !this.originalSelection) return false;
// 恢复原始选区和羽化值
this.selectionManager.setSelectionFromPath(this.originalSelection);
this.selectionManager.setFeatherAmount(this.originalFeatherAmount);
return true;
}
}
/**
* 填充选区命令
*/
export class FillSelectionCommand extends Command {
constructor(options = {}) {
super({
name: "填充选区",
description: "使用指定颜色填充当前选区",
saveState: false,
});
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.selectionManager = options.selectionManager;
this.color = options.color || "#000000";
this.targetLayerId = options.targetLayerId;
this.createdObjectIds = [];
}
async execute() {
if (!this.canvas || !this.layerManager || !this.selectionManager) {
console.error("无法填充选区:参数无效");
return false;
}
// 获取选区路径
const selectionPath = this.selectionManager.getSelectionObject();
if (!selectionPath) {
console.error("无法填充选区:当前没有选区");
return false;
}
// 确定目标图层
const layerId = this.targetLayerId || this.layerManager.getActiveLayerId();
if (!layerId) {
console.error("无法填充选区:没有活动图层");
return false;
}
// 创建填充对象
const fillObject = new fabric.Path(selectionPath.path, {
fill: this.color,
stroke: null,
opacity: 1,
id: `fill_${Date.now()}_${Math.floor(Math.random() * 1000)}`,
layerId: layerId,
selectable: false,
});
// 应用羽化效果(如果有)
const featherAmount = this.selectionManager.getFeatherAmount();
if (featherAmount > 0) {
fillObject.shadow = new fabric.Shadow({
color: this.color,
blur: featherAmount,
offsetX: 0,
offsetY: 0,
});
}
// 添加到图层
this.layerManager.addObjectToLayer(layerId, fillObject);
this.createdObjectIds.push(fillObject.id);
// 清空选区
this.selectionManager.clearSelection();
return true;
}
async undo() {
if (!this.layerManager || this.createdObjectIds.length === 0) return false;
// 移除创建的填充对象
for (const id of this.createdObjectIds) {
const layerObj = this._findObjectInLayers(id);
if (layerObj) {
this.layerManager.removeObjectFromLayer(layerObj.layerId, id);
}
}
return true;
}
_findObjectInLayers(objectId) {
if (!this.layerManager) return null;
const layers = this.layerManager.layers.value;
for (const layer of layers) {
if (layer.fabricObjects) {
const obj = layer.fabricObjects.find((obj) => obj.id === objectId);
if (obj) return { object: obj, layerId: layer.id };
}
}
return null;
}
}
/**
* 复制选区内容到新图层命令
*/
export class CopySelectionToNewLayerCommand extends CompositeCommand {
constructor(options = {}) {
super([], {
name: "复制选区到新图层",
description: "将选区中的内容复制到新图层",
});
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.selectionManager = options.selectionManager;
this.sourceLayerId = options.sourceLayerId;
this.newLayerName = options.newLayerName || "选区复制";
this.newLayerId = null;
this.copiedObjectIds = [];
}
async execute() {
if (!this.canvas || !this.layerManager || !this.selectionManager) {
console.error("无法复制选区:参数无效");
return false;
}
try {
// 获取选区
const selectionObject = this.selectionManager.getSelectionObject();
if (!selectionObject) {
console.error("无法复制选区:当前没有选区");
return false;
}
// 确定源图层
const sourceId =
this.sourceLayerId || this.layerManager.getActiveLayerId();
const sourceLayer = this.layerManager.getLayerById(sourceId);
if (!sourceLayer || !sourceLayer.fabricObjects) {
console.error("无法复制选区:源图层无效或为空");
return false;
}
// 创建新图层
this.newLayerId = await this.layerManager.createLayer(
this.newLayerName,
LayerType.EMPTY
);
// 获取选区内的对象
const objectsToCopy = sourceLayer.fabricObjects.filter((obj) => {
return this.selectionManager.isObjectInSelection(obj);
});
if (objectsToCopy.length === 0) {
console.warn("选区内没有对象可复制");
return true; // 仍然返回成功,因为已创建了新图层
}
// 复制对象到新图层
for (const obj of objectsToCopy) {
// 克隆对象
const clonedObj = await this._cloneObject(obj);
// 设置新的ID和图层ID
const newId = `copy_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
clonedObj.id = newId;
clonedObj.layerId = this.newLayerId;
// 添加到新图层
await this.layerManager.addObjectToLayer(this.newLayerId, clonedObj);
this.copiedObjectIds.push(newId);
}
// 设置新图层为活动图层
this.layerManager.setActiveLayer(this.newLayerId);
// 清空选区
this.selectionManager.clearSelection();
return {
newLayerId: this.newLayerId,
copiedCount: this.copiedObjectIds.length,
};
} catch (error) {
console.error("复制选区过程中出错:", error);
// 如果已经创建了新图层,需要进行清理
if (this.newLayerId) {
try {
await this.layerManager.removeLayer(this.newLayerId);
} catch (cleanupError) {
console.warn("清理新图层失败:", cleanupError);
}
}
throw error;
}
}
async _cloneObject(obj) {
return new Promise((resolve, reject) => {
if (!obj) {
reject(new Error("对象无效,无法克隆"));
return;
}
try {
obj.clone((cloned) => {
resolve(cloned);
});
} catch (error) {
reject(error);
}
});
}
}
/**
* 从选区中删除内容命令
*/
export class ClearSelectionContentCommand extends Command {
constructor(options = {}) {
super({
name: "清除选区内容",
description: "删除选区中的内容",
saveState: false,
});
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.selectionManager = options.selectionManager;
this.targetLayerId = options.targetLayerId;
this.removedObjects = [];
}
async execute() {
if (!this.canvas || !this.layerManager || !this.selectionManager) {
console.error("无法清除选区内容:参数无效");
return false;
}
// 获取选区
const selectionObject = this.selectionManager.getSelectionObject();
if (!selectionObject) {
console.error("无法清除选区内容:当前没有选区");
return false;
}
// 确定目标图层
const layerId = this.targetLayerId || this.layerManager.getActiveLayerId();
const layer = this.layerManager.getLayerById(layerId);
if (!layer || !layer.fabricObjects) {
console.error("无法清除选区内容:目标图层无效或为空");
return false;
}
// 找到选区内的对象
const objectsToRemove = layer.fabricObjects.filter((obj) => {
return this.selectionManager.isObjectInSelection(obj);
});
if (objectsToRemove.length === 0) {
console.warn("选区内没有对象需要清除");
return true;
}
// 备份被删除的对象
this.removedObjects = objectsToRemove.map((obj) => ({
object: this._cloneObjectSync(obj),
layerId: layerId,
}));
// 从图层中移除对象
for (const obj of objectsToRemove) {
this.layerManager.removeObjectFromLayer(layerId, obj.id);
}
return { removedCount: objectsToRemove.length };
}
async undo() {
if (!this.layerManager || this.removedObjects.length === 0) return false;
// 恢复被删除的对象
for (const item of this.removedObjects) {
if (item.object && item.layerId) {
const clonedObj = await this._cloneObject(item.object);
this.layerManager.addObjectToLayer(item.layerId, clonedObj);
}
}
return true;
}
_cloneObjectSync(obj) {
// 这是一个简单的深拷贝,不适用于所有场景
// 在实际应用中应该使用fabric.js的clone方法
if (!obj) return null;
return JSON.parse(JSON.stringify(obj));
}
async _cloneObject(obj) {
return new Promise((resolve, reject) => {
if (!obj) {
reject(new Error("对象无效,无法克隆"));
return;
}
try {
if (typeof obj.clone === "function") {
obj.clone((cloned) => {
resolve(cloned);
});
} else {
// 如果对象没有clone方法(可能是因为它已经是序列化后的对象)
// 在实际代码中需要适当处理这种情况
resolve(Object.assign({}, obj));
}
} catch (error) {
reject(error);
}
});
}
}
// 导入套索抠图命令
export { LassoCutoutCommand } from "./LassoCutoutCommand.js";

View File

@@ -0,0 +1,134 @@
import { Command } from "./Command";
/**
* 对象变换命令
* 轻量级命令,只记录对象的变换属性变化(位置、缩放、旋转)
* 不保存整个对象或画布状态,只关注变换属性
*/
export class TransformCommand extends Command {
constructor(options) {
super({
name: options.name || "对象变换",
description: options.description || "移动、缩放或旋转对象",
saveState: false, // 自己管理状态,避免递归
});
this.canvas = options.canvas;
this.objectId = options.objectId;
this.initialState = options.initialState || null;
this.finalState = options.finalState || null;
this.objectType = options.objectType || "object";
}
/**
* 执行命令
* 如果是首次执行,记录初始和最终状态
* 如果是重做,应用最终状态
*/
execute() {
if (!this.finalState) {
console.warn("没有最终状态可应用");
return false;
}
// 查找目标对象
const targetObject = this._findObject(this.objectId);
if (!targetObject) {
console.warn(`未找到ID为 ${this.objectId} 的对象`);
return false;
}
// 应用最终变换状态
this._applyTransform(targetObject, this.finalState);
// 触发画布更新
this.canvas.renderAll();
return true;
}
/**
* 撤销命令
* 应用初始状态
*/
undo() {
if (!this.initialState) {
console.warn("没有初始状态可恢复");
return false;
}
// 查找目标对象
const targetObject = this._findObject(this.objectId);
if (!targetObject) {
console.warn(`未找到ID为 ${this.objectId} 的对象`);
return false;
}
// 应用初始变换状态
this._applyTransform(targetObject, this.initialState);
// 触发画布更新
this.canvas.renderAll();
return true;
}
/**
* 查找对象
* @private
*/
_findObject(objectId) {
if (!this.canvas) return null;
return this.canvas.getObjects().find((obj) => obj.id === objectId);
}
/**
* 应用变换状态到对象
* @private
*/
_applyTransform(object, transformState) {
if (!object || !transformState) return;
// 应用变换属性,只设置真正变化的值
Object.entries(transformState).forEach(([key, value]) => {
object.set(key, value);
});
// 确保对象更新
object.setCoords();
}
/**
* 获取命令信息
*/
getInfo() {
return {
name: this.name,
description: this.description,
objectId: this.objectId,
objectType: this.objectType,
changedProps: this.finalState ? Object.keys(this.finalState) : [],
};
}
/**
* 捕获对象的变换状态
* @static
*/
static captureTransformState(object) {
if (!object) return null;
// 只捕获变换相关的属性
return {
left: object.left,
top: object.top,
scaleX: object.scaleX,
scaleY: object.scaleY,
angle: object.angle,
flipX: object.flipX,
flipY: object.flipY,
skewX: object.skewX,
skewY: object.skewY,
};
}
}

View File

@@ -0,0 +1,304 @@
import { Command } from "./Command";
/**
* 文本内容命令
* 用于更改文本图层的文本内容
*/
export class TextContentCommand extends Command {
constructor(options) {
super({
name: "修改文本内容",
description: "修改文本图层的文本内容",
});
this.canvas = options.canvas;
this.textObject = options.textObject;
this.newText = options.newText;
this.oldText = this.textObject.text;
}
execute() {
this.textObject.set("text", this.newText);
this.canvas.renderAll();
return true;
}
undo() {
this.textObject.set("text", this.oldText);
this.canvas.renderAll();
return true;
}
}
/**
* 文本字体命令
* 用于更改文本图层的字体
*/
export class TextFontCommand extends Command {
constructor(options) {
super({
name: "修改文本字体",
description: "修改文本图层的字体",
});
this.canvas = options.canvas;
this.textObject = options.textObject;
this.newFont = options.newFont;
this.oldFont = this.textObject.fontFamily;
}
execute() {
this.textObject.set("fontFamily", this.newFont);
this.canvas.renderAll();
return true;
}
undo() {
this.textObject.set("fontFamily", this.oldFont);
this.canvas.renderAll();
return true;
}
}
/**
* 文本尺寸命令
* 用于更改文本图层的字体大小
*/
export class TextSizeCommand extends Command {
constructor(options) {
super({
name: "修改文本尺寸",
description: "修改文本图层的字体大小",
});
this.canvas = options.canvas;
this.textObject = options.textObject;
this.newSize = options.newSize;
this.oldSize = this.textObject.fontSize;
}
execute() {
this.textObject.set("fontSize", this.newSize);
this.canvas.renderAll();
return true;
}
undo() {
this.textObject.set("fontSize", this.oldSize);
this.canvas.renderAll();
return true;
}
}
/**
* 文本颜色命令
* 用于更改文本图层的颜色
*/
export class TextColorCommand extends Command {
constructor(options) {
super({
name: "修改文本颜色",
description: "修改文本图层的颜色",
});
this.canvas = options.canvas;
this.textObject = options.textObject;
this.newColor = options.newColor;
this.oldColor = this.textObject.fill;
}
execute() {
this.textObject.set("fill", this.newColor);
this.canvas.renderAll();
return true;
}
undo() {
this.textObject.set("fill", this.oldColor);
this.canvas.renderAll();
return true;
}
}
/**
* 文本对齐方式命令
* 用于更改文本图层的对齐方式
*/
export class TextAlignCommand extends Command {
constructor(options) {
super({
name: "修改文本对齐",
description: "修改文本图层的对齐方式",
});
this.canvas = options.canvas;
this.textObject = options.textObject;
this.newAlign = options.newAlign;
this.oldAlign = this.textObject.textAlign;
}
execute() {
this.textObject.set("textAlign", this.newAlign);
this.canvas.renderAll();
return true;
}
undo() {
this.textObject.set("textAlign", this.oldAlign);
this.canvas.renderAll();
return true;
}
}
/**
* 文本样式命令
* 用于更改文本图层的样式(粗体、斜体、下划线等)
*/
export class TextStyleCommand extends Command {
constructor(options) {
super({
name: "修改文本样式",
description: "修改文本图层的样式",
});
this.canvas = options.canvas;
this.textObject = options.textObject;
this.property = options.property; // 'fontWeight', 'fontStyle', 'underline', 'linethrough', 'overline'
this.newValue = options.newValue;
this.oldValue = this.textObject[this.property];
}
execute() {
this.textObject.set(this.property, this.newValue);
this.canvas.renderAll();
return true;
}
undo() {
this.textObject.set(this.property, this.oldValue);
this.canvas.renderAll();
return true;
}
}
/**
* 文本间距命令
* 用于更改文本图层的字符间距或行高
*/
export class TextSpacingCommand extends Command {
constructor(options) {
super({
name: "修改文本间距",
description: "修改文本图层的字符间距或行高",
});
this.canvas = options.canvas;
this.textObject = options.textObject;
this.property = options.property; // 'charSpacing' 或 'lineHeight'
this.newValue = options.newValue;
this.oldValue = this.textObject[this.property];
}
execute() {
this.textObject.set(this.property, this.newValue);
this.canvas.renderAll();
return true;
}
undo() {
this.textObject.set(this.property, this.oldValue);
this.canvas.renderAll();
return true;
}
}
/**
* 文本背景颜色命令
* 用于更改文本图层的背景颜色
*/
export class TextBackgroundCommand extends Command {
constructor(options) {
super({
name: "修改文本背景",
description: "修改文本图层的背景颜色",
});
this.canvas = options.canvas;
this.textObject = options.textObject;
this.newColor = options.newColor;
this.oldColor = this.textObject.textBackgroundColor;
}
execute() {
this.textObject.set("textBackgroundColor", this.newColor);
this.canvas.renderAll();
return true;
}
undo() {
this.textObject.set("textBackgroundColor", this.oldColor);
this.canvas.renderAll();
return true;
}
}
/**
* 文本透明度命令
* 用于更改文本图层的透明度
*/
export class TextOpacityCommand extends Command {
constructor(options) {
super({
name: "修改文本透明度",
description: "修改文本图层的透明度",
});
this.canvas = options.canvas;
this.textObject = options.textObject;
this.newOpacity = options.newOpacity;
this.oldOpacity = this.textObject.opacity;
}
execute() {
this.textObject.set("opacity", this.newOpacity);
this.canvas.renderAll();
return true;
}
undo() {
this.textObject.set("opacity", this.oldOpacity);
this.canvas.renderAll();
return true;
}
}
/**
* 组合文本编辑命令
* 用于一次性应用多个文本属性更改
*/
export class CompositeTextCommand extends Command {
constructor(options) {
super({
name: "组合文本编辑",
description: "组合多个文本编辑操作",
});
this.canvas = options.canvas;
this.textObject = options.textObject;
this.changes = options.changes; // {property: newValue} 形式的对象
this.oldValues = {};
// 保存所有属性的旧值
for (const property in this.changes) {
if (this.textObject[property] !== undefined) {
this.oldValues[property] = this.textObject[property];
}
}
}
execute() {
for (const property in this.changes) {
this.textObject.set(property, this.changes[property]);
}
this.canvas.renderAll();
return true;
}
undo() {
for (const property in this.oldValues) {
this.textObject.set(property, this.oldValues[property]);
}
this.canvas.renderAll();
return true;
}
}

View File

@@ -0,0 +1,54 @@
import { Command } from "./Command";
/**
* 工具切换命令
* 用于切换编辑器的工具模式(如绘画、选择、橡皮擦等)
*/
export class ToolCommand extends Command {
/**
* 创建一个工具切换命令
* @param {Object} options 配置选项
* @param {Object} options.toolManager 工具管理器实例
* @param {String} options.tool 要设置的工具名称
* @param {String} options.previousTool 先前的工具名称(可选,如果不提供会在执行时记录)
* @param {Boolean} options.saveState 是否保存画布状态默认为false
*/
constructor(options) {
super({
...options,
name: `切换工具: ${options.tool}`,
description: `将工具切换为 ${options.tool}`,
});
this.toolManager = options.toolManager;
this.tool = options.tool;
this.previousTool = options.previousTool || null;
}
/**
* 执行工具切换
* @returns {String} 设置的工具名称
*/
execute() {
if (!this.toolManager) return null;
// 记录当前工具(用于撤销)
if (!this.previousTool) {
this.previousTool = this.toolManager.getCurrentTool();
}
// 切换工具
return this.toolManager.setTool(this.tool);
}
/**
* 撤销工具切换
* @returns {String} 恢复的工具名称
*/
undo() {
if (!this.toolManager || !this.previousTool) return null;
// 恢复到先前工具
return this.toolManager.setTool(this.previousTool);
}
}