Files
aida_front/src/component/Canvas/CanvasEditor/commands/LiquifyCommands.js

579 lines
16 KiB
JavaScript
Raw Normal View History

2025-06-09 10:25:54 +08:00
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);
}