合并画布代码

This commit is contained in:
X1627315083
2025-06-18 11:05:23 +08:00
parent 903c0ebdf5
commit 9c7fae36eb
118 changed files with 23633 additions and 8201 deletions

View File

@@ -1,5 +1,5 @@
import { Command } from "./Command";
//import { fabric } from "fabric-with-all";
import { fabric } from "fabric-with-all";
/**
* 创建背景图层命令
@@ -195,10 +195,13 @@ export class BackgroundSizeCommand extends Command {
this.newWidth = options.newWidth;
this.newHeight = options.newHeight;
this.historyManager = options.historyManager;
this.isRedGreenMode = options.isRedGreenMode;
this.bgLayer = this.layers.value.find((layer) => layer.isBackground);
// 记录原尺寸
this.oldWidth = this.canvas.width;
this.oldHeight = this.canvas.height;
this.oldWidth = this.bgLayer.fabricObject.width;
this.oldHeight = this.bgLayer.fabricObject.height;
// 查找背景图层
this.bgLayer = this.layers.value.find((layer) => layer.isBackground);

View File

@@ -609,12 +609,15 @@ export class TextureUploadCommand extends BaseBrushCommand {
constructor(options = {}) {
super({
...options,
name: `上传纹理: ${options.name || options.file?.name || '未知'}`,
name: `上传纹理: ${options.name || options.file?.name || "未知"}`,
description: `上传自定义纹理文件`,
});
this.file = options.file;
this.name = options.name || options.file?.name?.replace(/\.[^/.]+$/, "") || "自定义纹理";
this.name =
options.name ||
options.file?.name?.replace(/\.[^/.]+$/, "") ||
"自定义纹理";
this.category = options.category || "自定义材质";
this.texturePresetManager = options.texturePresetManager;
this.brushManager = options.brushManager;
@@ -623,13 +626,13 @@ export class TextureUploadCommand extends BaseBrushCommand {
async execute() {
if (!this.file || !this.texturePresetManager) {
throw new Error('缺少必要的文件或纹理预设管理器');
throw new Error("缺少必要的文件或纹理预设管理器");
}
try {
// 创建文件 data URL
const dataUrl = await this._fileToDataUrl(this.file);
// 添加到纹理预设管理器
this.uploadedTextureId = this.texturePresetManager.addCustomTexture({
name: this.name,
@@ -654,10 +657,10 @@ export class TextureUploadCommand extends BaseBrushCommand {
return {
textureId: this.uploadedTextureId,
dataUrl: dataUrl,
name: this.name
name: this.name,
};
} catch (error) {
console.error('纹理上传失败:', error);
console.error("纹理上传失败:", error);
throw new Error(`纹理上传失败: ${error.message}`);
}
}
@@ -672,7 +675,7 @@ export class TextureUploadCommand extends BaseBrushCommand {
this.texturePresetManager.removeCustomTexture(this.uploadedTextureId);
return true;
} catch (error) {
console.error('撤销纹理上传失败:', error);
console.error("撤销纹理上传失败:", error);
return false;
}
}
@@ -680,7 +683,7 @@ export class TextureUploadCommand extends BaseBrushCommand {
/**
* 将文件转换为 data URL
* @private
* @param {File} file
* @param {File} file
* @returns {Promise<string>}
*/
_fileToDataUrl(file) {

View File

@@ -0,0 +1,100 @@
import { isArray } from "lodash-es";
import { optimizeCanvasRendering } from "../utils/helper";
import { restoreObjectLayerAssociations } from "../utils/layerUtils";
import { Command } from "./Command";
/**
* 橡皮擦操作命令
* 支持橡皮擦操作的撤销和重做
*/
export class EraserCommand extends Command {
/**
* 构造函数
* @param {Object} options 命令选项
* @param {Object} options.canvas Fabric画布实例
* @param {Object} options.layerManager 图层管理器
* @param {Object} options.beforeSnapshot 擦除前的状态快照
* @param {Object} options.afterSnapshot 擦除后的状态快照
* @param {Array} options.affectedObjects 擦除影响的对象列表(可选)
*/
constructor(options) {
super({
name: "橡皮擦操作",
description: `擦除`,
...options,
});
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.beforeSnapshot = options.beforeSnapshot;
this.afterSnapshot = options.afterSnapshot;
this.affectedObjects = options.affectedObjects || [];
this.fristLoad = true; // 是否是第一次加载
}
/**
* 执行橡皮擦操作
*/
async execute() {
if (!this.beforeSnapshot || !this.afterSnapshot) {
console.warn("缺少状态快照,无法执行橡皮擦命令");
return false;
}
if (!this.fristLoad)
await this._restoreCanvasState(this.afterSnapshot); // 应用重做的状态
else await this.layerManager?.updateLayersObjectsInteractivity?.(false);
this.fristLoad = false; // 标记为非第一次加载
return true;
}
/**
* 撤销橡皮擦操作
*/
async undo() {
if (!this.beforeSnapshot) {
console.warn("缺少擦除前的状态快照,无法撤销");
return false;
}
// 恢复到擦除前的状态
await this._restoreCanvasState(this.beforeSnapshot);
return true;
}
/**
* 恢复画布状态
* @param {Object} snapshot 状态快照
* @private
*/
async _restoreCanvasState(snapshot) {
// 对比 eraser erasable 两个属性如果当前对象的eraser属性和erasable属性不一致则需要更新对象的eraser属性
if (!snapshot || !snapshot.objects) return;
// 优化渲染 - 统一批处理 支持异步回调 防止闪屏
await optimizeCanvasRendering(this.canvas, async () => {
return new Promise((resolve) => {
this.canvas.loadFromJSON(snapshot, async () => {
// 恢复图层关联
this._restoreObjectLayerAssociations();
// 确保所有对象的交互性正确设置
await this.layerManager?.updateLayersObjectsInteractivity?.(false);
resolve();
});
});
});
}
/**
* 恢复对象与图层的关联关系
* @private
*/
_restoreObjectLayerAssociations() {
if (!this.layerManager) return;
const canvasObjects = this.canvas.getObjects();
restoreObjectLayerAssociations(
this.layerManager?.layers?.value,
canvasObjects
);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@ 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";
import { fabric } from "fabric-with-all";
/**
* 套索抠图命令

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
import { Command, FunctionCommand } from "./Command";
import { getLiquifyReferenceManager } from "../managers/LiquifyReferenceManager";
/**
* 液化命令基类
@@ -192,81 +193,6 @@ export class LiquifyCommand extends Command {
}
}
/**
* 图层栅格化命令
* 用于将复杂图层栅格化为单一图像,以便进行液化操作
*/
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;
}
}
/**
* 液化工具初始化命令
* 用于初始化液化工具的状态,不执行实际操作
@@ -550,6 +476,405 @@ export class LiquifyResetCommand extends LiquifyCommand {
}
}
/**
* 液化状态命令 - 最优化版本
* 使用引用管理器避免对象引用丢失,支持高性能的状态管理
*/
export class LiquifyStateCommand extends Command {
/**
* 创建液化状态命令
* @param {Object} options 配置选项
* @param {Object} options.canvas Fabric.js画布实例
* @param {Object} options.layerManager 图层管理器实例
* @param {Object} options.targetObject 目标对象(保持引用)
* @param {String} options.targetLayerId 目标图层ID
* @param {ImageData} options.initialImageData 初始图像数据
* @param {ImageData} options.finalImageData 最终图像数据
*/
constructor(options) {
super({
name: options.name || "液化操作",
description: options.description || "液化变形操作的状态记录",
});
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.targetObject = options.targetObject;
this.targetLayerId = options.targetLayerId;
this.targetObjectId = options.targetObjectId;
// 获取引用管理器实例
this.refManager = getLiquifyReferenceManager();
// 注册对象到引用管理器
this.objectRefId = this.refManager.registerObject(
this.targetObject,
this.targetObjectId || `liquify_${Date.now()}`
);
// 保存状态快照ID
this.initialSnapshotId = null;
this.finalSnapshotId = null;
// 设置图像数据
this.initialImageData = options.initialImageData;
this.finalImageData = options.finalImageData;
this.currentState = "initial";
// 创建初始快照
if (this.initialImageData) {
this._createInitialSnapshot();
}
}
/**
* 执行命令 - 应用最终状态
*/
async execute() {
if (!this.finalImageData) {
throw new Error("缺少最终状态数据");
}
// 确保有最终状态快照
if (!this.finalSnapshotId) {
await this._createFinalSnapshot();
}
// 通过引用管理器更新图像数据
await this.refManager.updateObjectImageData(
this.objectRefId,
this.finalImageData
);
this.currentState = "final";
this.canvas.renderAll();
console.log("✅ 液化命令执行完成,应用最终状态");
return true;
}
/**
* 撤销命令 - 恢复初始状态
*/
async undo() {
if (!this.initialImageData || !this.initialSnapshotId) {
throw new Error("缺少初始状态数据或快照");
}
// 通过引用管理器恢复到初始快照
await this.refManager.restoreFromSnapshot(
this.objectRefId,
this.initialSnapshotId
);
this.currentState = "initial";
this.canvas.renderAll();
console.log("🔄 液化命令撤销完成,恢复初始状态");
return true;
}
/**
* 重做命令 - 重新应用最终状态
*/
async redo() {
return this.execute();
}
/**
* 设置最终状态的图像数据
* @param {ImageData} finalImageData 最终状态的图像数据
*/
setFinalImageData(finalImageData) {
this.finalImageData = finalImageData;
this.finalSnapshotId = null; // 重置快照ID下次执行时重新创建
}
/**
* 获取当前状态标记
* @returns {String} 'initial' 或 'final'
*/
getCurrentState() {
return this.currentState;
}
/**
* 检查命令是否有效
* @returns {Boolean} 是否有效
*/
isValid() {
const targetObject = this.refManager.getObjectRef(this.objectRefId);
return !!(targetObject && this.initialImageData && this.finalImageData);
}
/**
* 获取目标对象的当前引用
* @returns {Object|null} Fabric对象
*/
getTargetObject() {
return this.refManager.getObjectRef(this.objectRefId);
}
/**
* 清理资源
*/
dispose() {
if (this.objectRefId) {
// 清理快照
if (this.initialSnapshotId) {
this.refManager.stateSnapshots.delete(this.initialSnapshotId);
}
if (this.finalSnapshotId) {
this.refManager.stateSnapshots.delete(this.finalSnapshotId);
}
// 注意:不要清理对象引用,因为可能有其他命令在使用
console.log(`🗑️ 液化状态命令资源已清理: ${this.objectRefId}`);
}
}
/**
* 获取内存使用统计
* @returns {Object} 统计信息
*/
getMemoryStats() {
const refManagerStats = this.refManager.getMemoryStats();
const commandMemory = this._calculateCommandMemory();
return {
...refManagerStats,
commandMemory,
totalCommandMemory: commandMemory,
};
}
// 私有方法
/**
* 创建初始状态快照
* @private
*/
async _createInitialSnapshot() {
if (!this.initialImageData) return;
this.initialSnapshotId = `${this.objectRefId}_initial_${Date.now()}`;
// 手动创建初始快照,包含图像数据
const fabricObject = this.refManager.getObjectRef(this.objectRefId);
if (fabricObject) {
const snapshot = {
timestamp: Date.now(),
properties: this.refManager._captureObjectState(fabricObject),
imageData: this.initialImageData,
eventListeners:
this.refManager.eventListeners.get(this.objectRefId) || {},
};
this.refManager.stateSnapshots.set(this.initialSnapshotId, snapshot);
console.log(`📸 初始状态快照已创建: ${this.initialSnapshotId}`);
}
}
/**
* 创建最终状态快照
* @private
*/
async _createFinalSnapshot() {
if (!this.finalImageData) return;
this.finalSnapshotId = `${this.objectRefId}_final_${Date.now()}`;
// 手动创建最终快照,包含图像数据
const fabricObject = this.refManager.getObjectRef(this.objectRefId);
if (fabricObject) {
const snapshot = {
timestamp: Date.now(),
properties: this.refManager._captureObjectState(fabricObject),
imageData: this.finalImageData,
eventListeners:
this.refManager.eventListeners.get(this.objectRefId) || {},
};
this.refManager.stateSnapshots.set(this.finalSnapshotId, snapshot);
console.log(`📸 最终状态快照已创建: ${this.finalSnapshotId}`);
}
}
/**
* 计算命令本身的内存使用量
* @returns {Number} 内存使用量(字节)
* @private
*/
_calculateCommandMemory() {
let bytes = 0;
// 计算ImageData内存使用
if (this.initialImageData) {
bytes += this.initialImageData.width * this.initialImageData.height * 4;
}
if (this.finalImageData) {
bytes += this.finalImageData.width * this.finalImageData.height * 4;
}
return bytes;
}
}
/**
* 批量液化状态命令 - 用于处理多个对象的液化操作
*/
export class BatchLiquifyStateCommand extends Command {
/**
* 创建批量液化状态命令
* @param {Object} options 配置选项
* @param {Array} options.commands 液化状态命令列表
* @param {Object} options.canvas Fabric.js画布实例
*/
constructor(options) {
super({
name: options.name || "批量液化操作",
description:
options.description || `批量液化${options.commands?.length || 0}个对象`,
});
this.commands = options.commands || [];
this.canvas = options.canvas;
this.refManager = getLiquifyReferenceManager();
}
/**
* 添加液化状态命令
* @param {LiquifyStateCommand} command 液化状态命令
*/
addCommand(command) {
this.commands.push(command);
}
/**
* 执行批量操作
*/
async execute() {
const results = [];
const updates = [];
// 准备批量更新数据
for (const command of this.commands) {
if (command.finalImageData && command.objectRefId) {
updates.push({
refId: command.objectRefId,
imageData: command.finalImageData,
});
}
}
// 执行批量更新
if (updates.length > 0) {
const updateResults = await this.refManager.batchUpdate(updates);
results.push(...updateResults);
}
// 更新命令状态
this.commands.forEach((command) => {
command.currentState = "final";
});
this.canvas.renderAll();
return results;
}
/**
* 撤销批量操作
*/
async undo() {
// 逆序撤销
for (let i = this.commands.length - 1; i >= 0; i--) {
await this.commands[i].undo();
}
this.canvas.renderAll();
return true;
}
/**
* 重做批量操作
*/
async redo() {
return this.execute();
}
/**
* 检查批量命令是否有效
* @returns {Boolean} 是否有效
*/
isValid() {
return (
this.commands.length > 0 && this.commands.every((cmd) => cmd.isValid())
);
}
/**
* 清理所有子命令的资源
*/
dispose() {
this.commands.forEach((command) => {
if (command.dispose) {
command.dispose();
}
});
}
}
/**
* 序列化fabric对象为JSON
* @param {Object} fabricObject fabric对象
* @returns {Object} 序列化后的对象状态
*/
export function serializeFabricObject(fabricObject) {
if (!fabricObject) {
throw new Error("无法序列化:对象为空");
}
try {
// 使用fabric.js的toObject方法序列化
const serializedObject = fabricObject.toObject([
"id",
"objectId",
"uid",
"layerId",
"name",
"type",
]);
// 记录额外的元数据
const metadata = {
timestamp: Date.now(),
objectType: fabricObject.type,
objectId: fabricObject.id || fabricObject.objectId || fabricObject.uid,
layerId: fabricObject.layerId,
bounds: fabricObject.getBoundingRect(),
};
return {
serializedObject,
metadata,
version: "1.0",
};
} catch (error) {
console.error("序列化fabric对象失败:", error);
throw error;
}
}
/**
* 辅助函数:创建液化状态命令
* @param {Object} options 配置选项
* @returns {LiquifyStateCommand} 状态命令实例
*/
export function createLiquifyStateCommand(options) {
return new LiquifyStateCommand(options);
}
/**
* 辅助函数:创建液化重置命令
* @param {Object} options 配置选项

View File

@@ -1,6 +1,11 @@
import { OperationType } from "../utils/layerHelper.js";
import {
findObjectById,
generateId,
optimizeCanvasRendering,
} from "../utils/helper.js";
import { LayerType, OperationType } from "../utils/layerHelper.js";
import { Command, CompositeCommand } from "./Command.js";
//import { fabric } from "fabric-with-all";
import { fabric } from "fabric-with-all";
/**
* 批量初始化红绿图模式命令
@@ -29,100 +34,134 @@ export class BatchInitializeRedGreenModeCommand extends Command {
this.originalNormalObjects = null;
this.originalNormalOpacities = new Map();
this.originalToolState = null;
this.originalActiveLayerId = null;
// 存储加载的图片对象
this.clothingImage = null;
this.redGreenImage = null;
// 存储新创建的图层ID
this.newEmptyLayerId = null;
}
async execute() {
try {
// 禁用画布渲染以避免闪烁
this.canvas.renderOnAddRemove = false;
await optimizeCanvasRendering(this.canvas, async () => {
// 1. 设置画布背景为白色
this.originalCanvasBackground = this.canvas.backgroundColor;
this.canvas.setBackgroundColor("#ffffff", () => {});
// 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
);
// 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 (!backgroundLayer || !fixedLayer || normalLayers.length === 0) {
throw new Error("缺少必要的图层结构");
}
});
// 保存工具状态
if (this.toolManager) {
this.originalToolState = {
currentTool: this.toolManager.getCurrentTool(),
isRedGreenMode: this.toolManager.isRedGreenMode,
};
}
const normalLayer = normalLayers[0]; // 使用第一个普通图层
// 4. 确保背景图层大小正确
await this._setupBackgroundLayer(backgroundLayer);
// 3. 保存原始状态
this.originalBackgroundObject = backgroundLayer.fabricObject
? {
...backgroundLayer.fabricObject.toObject(),
ref: backgroundLayer.fabricObject,
}
: null;
// 5. 并行加载两个图片
const [clothingImg, redGreenImg] = await Promise.all([
this._loadImage(this.clothingImageUrl),
this._loadImage(this.redGreenImageUrl)
]);
this.originalFixedObjects = fixedLayer.fabricObject
? [fixedLayer.fabricObject]
: [];
// 6. 设置衣服底图到固定图层
await this._setupClothingImage(clothingImg, fixedLayer);
this.originalNormalObjects = normalLayer.fabricObjects
? [...normalLayer.fabricObjects]
: [];
// 7. 设置红绿图到普通图层,位置和大小与衣服底图一致
await this._setupRedGreenImage(redGreenImg, normalLayer, this.clothingImage);
// 保存当前活动图层ID
this.originalActiveLayerId = this.layerManager.getActiveLayerId();
// 8. 设置普通图层透明度
this._setupNormalLayerOpacity(normalLayers);
// 保存普通图层透明度
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
);
});
}
});
// 9. 配置工具管理器
this._setupToolManager();
// 保存工具状态
if (this.toolManager) {
this.originalToolState = {
currentTool: this.toolManager.getCurrentTool(),
isRedGreenMode: this.toolManager.isRedGreenMode,
};
}
// 10. 重新启用渲染并执行一次性渲染
this.canvas.renderOnAddRemove = true;
this.canvas.renderAll();
// 5. 并行加载两个图片
const [clothingImg, redGreenImg] = await Promise.all([
this._loadImage(this.clothingImageUrl),
this._loadImage(this.redGreenImageUrl),
]);
console.log("批量红绿图模式初始化完成", {
衣服底图: this.clothingImageUrl,
红绿图: this.redGreenImageUrl,
普通图层透明度: `${Math.round(this.normalLayerOpacity * 100)}%`,
画布背景: "白色",
// 6. 设置衣服底图到固定图层
await this._setupClothingImage(clothingImg, fixedLayer);
// 7. 设置红绿图到普通图层,位置和大小与衣服底图一致
await this._setupRedGreenImage(
redGreenImg,
normalLayer,
this.clothingImage
);
// 4. 确保背景图层大小和衣服地图大小一致
await this._setupBackgroundLayer(backgroundLayer, this.clothingImage);
// 8. 设置普通图层透明度
this._setupNormalLayerOpacity(normalLayers); // 这里不需要在这里设置透明度 由图层统一处理
// 9. 创建新的空白图层并设置为活动图层
this.newEmptyLayerId = await this._createAndActivateEmptyLayer();
// 设置普通图层的裁剪对象为衣服底图
if (this.redGreenImage) {
// const clipPathImg = this.redGreenImage;
// clipPathImg.set({
// absolutePositioned: true,
// });
this.redGreenImage.set({
absolutePositioned: true,
});
const activeLayer = this.layerManager.getActiveLayer();
activeLayer.clippingMask = this.redGreenImage.toObject(["id"]);
activeLayer.opacity = this.normalLayerOpacity;
// activeLayer?.fabricObjects.forEach((obj) => {
// obj.set({
// clipPath: clipPathImg,
// });
// });
}
// 10. 配置工具管理器
this._setupToolManager();
console.log("批量红绿图模式初始化完成", {
衣服底图: this.clothingImageUrl,
红绿图: this.redGreenImageUrl,
普通图层透明度: `${Math.round(this.normalLayerOpacity * 100)}%`,
画布背景: "白色",
新建空图层ID: this.newEmptyLayerId,
});
await this.layerManager.updateLayersObjectsInteractivity(false);
});
return true;
@@ -134,86 +173,125 @@ export class BatchInitializeRedGreenModeCommand extends Command {
}
}
/**
* 创建新的空白图层并设置为活动图层
* @returns {Promise<string>} 新创建的图层ID
* @private
*/
async _createAndActivateEmptyLayer() {
// 创建新的空白图层
const newLayerName = "绘制图层";
const newLayerId = this.layerManager.createLayer(
newLayerName,
LayerType.GROUP,
{
undoable: false,
}
);
// 设置为活动图层
if (newLayerId) {
this.layerManager.setActiveLayer(newLayerId);
}
return newLayerId;
}
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);
await optimizeCanvasRendering(this.canvas, async () => {
// 1. 恢复画布背景
if (this.originalCanvasBackground !== null) {
this.canvas.setBackgroundColor(
this.originalCanvasBackground,
() => {}
);
}
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);
}
// 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 (this.newEmptyLayerId) {
const emptyLayerIndex = layers.findIndex(
(layer) => layer.id === this.newEmptyLayerId
);
if (emptyLayerIndex !== -1) {
layers.splice(emptyLayerIndex, 1);
}
}
// 恢复背景图层
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);
});
}
});
// 4. 恢复工具状态
if (this.toolManager && this.originalToolState) {
this.toolManager.isRedGreenMode = this.originalToolState.isRedGreenMode;
if (this.originalToolState.currentTool) {
this.toolManager.setTool(this.originalToolState.currentTool);
// 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);
}
});
}
});
// 恢复活动图层
if (this.originalActiveLayerId) {
this.layerManager.setActiveLayer(this.originalActiveLayerId);
}
}
// 5. 重新启用渲染
this.canvas.renderOnAddRemove = true;
this.canvas.renderAll();
// 4. 恢复工具状态
if (this.toolManager && this.originalToolState) {
this.toolManager.isRedGreenMode =
this.originalToolState.isRedGreenMode;
if (this.originalToolState.currentTool) {
this.toolManager.setTool(this.originalToolState.currentTool);
}
}
});
return true;
} catch (error) {
@@ -226,35 +304,42 @@ export class BatchInitializeRedGreenModeCommand extends Command {
/**
* 设置背景图层
*/
async _setupBackgroundLayer(backgroundLayer) {
async _setupBackgroundLayer(backgroundLayer, clothingImage) {
let backgroundObject = backgroundLayer.fabricObject;
const { object } = findObjectById(this.canvas, backgroundObject.id);
if (!backgroundObject) {
if (!object) {
// 创建白色背景矩形
backgroundObject = new fabric.Rect({
left: 0,
top: 0,
width: this.canvas.width,
height: this.canvas.height,
fill: "#ffffff",
object = new fabric.Rect({
left: this.canvas.width / 2,
top: this.canvas.height / 2,
width: clothingImage.width,
height: clothingImage.height,
scaleX: clothingImage.scaleX,
scaleY: clothingImage.scaleY,
fill: "transparent", // 确保背景是透明的
selectable: false,
evented: false,
isBackground: true,
layerId: backgroundLayer.id,
layerName: backgroundLayer.name,
originX: "center",
originY: "center",
});
this.canvas.add(backgroundObject);
this.canvas.sendToBack(backgroundObject);
backgroundLayer.fabricObject = backgroundObject;
this.canvas.add(object);
this.canvas.sendToBack(object);
backgroundLayer.fabricObject = object;
} else {
// 更新现有背景对象大小
backgroundObject.set({
width: this.canvas.width,
height: this.canvas.height,
left: 0,
top: 0,
fill: "#ffffff", // 确保背景是白色
object.set({
width: clothingImage.width,
height: clothingImage.height,
scaleX: clothingImage.scaleX,
scaleY: clothingImage.scaleY,
left: this.canvas.width / 2,
top: this.canvas.height / 2,
fill: "transparent", // 确保背景是透明的
});
}
}
@@ -299,6 +384,7 @@ export class BatchInitializeRedGreenModeCommand extends Command {
evented: false,
layerId: fixedLayer.id,
layerName: fixedLayer.name,
id: generateId("clothingImage"),
});
// 清除固定图层原有内容
@@ -332,6 +418,7 @@ export class BatchInitializeRedGreenModeCommand extends Command {
evented: false,
layerId: normalLayer.id,
layerName: normalLayer.name,
id: generateId("redGreenImage"),
});
// 清除普通图层原有内容
@@ -341,6 +428,8 @@ export class BatchInitializeRedGreenModeCommand extends Command {
});
}
// 给img设置裁剪裁剪图为衣服底图
// 添加到画布和普通图层
this.canvas.add(img);
normalLayer.fabricObjects = [img];
@@ -354,13 +443,6 @@ export class BatchInitializeRedGreenModeCommand extends Command {
normalLayers.forEach((layer) => {
// 设置图层透明度
layer.opacity = this.normalLayerOpacity;
// 更新图层中所有对象的透明度
if (layer.fabricObjects) {
layer.fabricObjects.forEach((obj) => {
obj.opacity = this.normalLayerOpacity;
});
}
});
}

View File

@@ -1,5 +1,5 @@
import { Command, CompositeCommand } from "./Command.js";
//import { fabric } from "fabric-with-all";
import { fabric } from "fabric-with-all";
import { createLayer, LayerType } from "../utils/layerHelper.js";
/**