import { findObjectById, generateId, optimizeCanvasRendering, } from "../utils/helper.js"; import { imageModeHandler } from "../utils/imageHelper.js"; import { LayerType, 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.clothingImageOpts = options.clothingImageOpts || null; // 衣服底图选项 - 用于设置图片加载时的选项 // 存储原始状态以便撤销 this.originalCanvasBackground = null; this.originalBackgroundObject = null; this.originalFixedObjects = null; this.originalNormalObjects = null; this.originalNormalOpacities = new Map(); this.originalToolState = null; this.originalActiveLayerId = null; // 存储加载的图片对象 this.clothingImage = null; this.redGreenImage = null; this.redGreenImageMask = null; // 存储新创建的图层ID this.newEmptyLayerId = null; } async execute() { try { await optimizeCanvasRendering(this.canvas, async () => { // 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, ref: backgroundLayer.fabricObject, } : null; this.originalFixedObjects = fixedLayer.fabricObject ? [fixedLayer.fabricObject] : []; this.originalNormalObjects = normalLayer.fabricObjects ? [...normalLayer.fabricObjects] : []; // 保存当前活动图层ID this.originalActiveLayerId = this.layerManager.getActiveLayerId(); // 保存普通图层透明度 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, }; } // 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 ); // 4. 确保背景图层大小和衣服地图大小一致 const backgroundObject = await this._setupBackgroundLayer( backgroundLayer, this.clothingImage ); // 8. 设置普通图层透明度 this._setupNormalLayerOpacity(normalLayers); // 这里不需要在这里设置透明度 由图层统一处理 // 9. 创建新的空白图层并设置为活动图层 // this.newEmptyLayerId = await this._createAndActivateEmptyLayer(); this.newEmptyLayerId = normalLayers[0]?.id || null; // 设置普通图层的裁剪对象为衣服底图 if (backgroundObject) { // const clipPathImg = this.redGreenImage; // clipPathImg.set({ // absolutePositioned: true, // }); // 克隆衣服底图作为裁剪对象 this.redGreenImageMask = await new Promise((resolve, reject) => { backgroundObject.clone((clonedImg) => { if (!clonedImg) { reject(new Error("无法克隆红绿图")); return; } resolve(clonedImg); }); }); this.redGreenImageMask.set({ absolutePositioned: true, opacity: 0.01, // 设置为几乎透明 type: "redGreenImageMask", id: generateId("redGreenImageMask_"), }); // this.canvas.add(this.redGreenImageMask); this.canvas.clipPath = this.redGreenImageMask; this.redGreenImageMask.sendToBack(); this.redGreenImageMask.setCoords(); const activeLayer = this.layerManager.getActiveLayer(); // activeLayer.clippingMask = this.redGreenImageMask.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; } catch (error) { // 恢复渲染 this.canvas.renderOnAddRemove = true; console.error("批量红绿图模式初始化失败:", error); throw error; } } /** * 创建新的空白图层并设置为活动图层 * @returns {Promise} 新创建的图层ID * @private */ async _createAndActivateEmptyLayer() { // 创建新的空白图层 const newLayerName = "绘制图层"; const newLayerId = await this.layerManager.createLayer( newLayerName, LayerType.BITMAP, { undoable: false, } ); // 设置为活动图层 if (newLayerId) { this.layerManager.setActiveLayer(newLayerId); } return newLayerId; } async undo() { try { await optimizeCanvasRendering(this.canvas, async () => { // 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 (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); }); } // 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); } // 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) { this.canvas.renderOnAddRemove = true; console.error("撤销批量红绿图模式初始化失败:", error); return false; } } /** * 设置背景图层 */ async _setupBackgroundLayer(backgroundLayer, clothingImage) { let backgroundObject = backgroundLayer.fabricObject; let { object } = findObjectById(this.canvas, backgroundObject.id); if (!object) { // 创建白色背景矩形 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(object); this.canvas.sendToBack(object); backgroundLayer.fabricObject = object.toObject(["id", "layerId", "type"]); } else { // 更新现有背景对象大小 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", // 确保背景是透明的 }); } object.setCoords(); return object; } /** * 加载图片 */ 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) { if (this.clothingImageOpts?.imageMode) { // 如果有衣服底图选项,应用这些选项 // 底图加载方式 1.平铺 2.拉伸 3.拉伸平铺 4.拉伸平铺并裁剪 5.包含 // this.clothingImageOpts?.imageMode // 默认不处理 可选 contains, stretch,tile, stretchTile, stretchTileCrop // 通用处理图片模式 imageModeHandler({ imageMode: this.clothingImageOpts?.imageMode, newImage: img, canvasWidth: this.canvas.width, canvasHeight: this.canvas.height, }); } else { // 计算图片缩放,保持上下留边距 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, }); } img.set({ left: this.canvas.width / 2, top: this.canvas.height / 2, originX: "center", originY: "center", selectable: false, evented: false, layerId: fixedLayer.id, layerName: fixedLayer.name, id: generateId("clothingImage"), }); // 清除固定图层原有内容 if (fixedLayer.fabricObject) { const { object } = findObjectById( this.canvas, fixedLayer.fabricObject.id ); if (object) this.canvas.remove(object); } // 添加到画布和固定图层 this.canvas.add(img); fixedLayer.fabricObject = img.toObject(["id", "type", "layerId"]); this.clothingImage = img; } /** * 设置红绿图 */ async _setupRedGreenImage(img, normalLayer, clothingImage) { if (!clothingImage) { throw new Error("衣服底图未加载,无法设置红绿图位置"); } if (this.clothingImageOpts?.imageMode) { imageModeHandler({ imageMode: this.clothingImageOpts?.imageMode, newImage: img, canvasWidth: this.canvas.width, canvasHeight: this.canvas.height, }); } else { // 使用与衣服底图完全相同的属性 img.set({ scaleX: clothingImage.scaleX, scaleY: clothingImage.scaleY, }); } img.set({ left: clothingImage.left, top: clothingImage.top, originX: clothingImage.originX, originY: clothingImage.originY, selectable: false, evented: false, layerId: normalLayer.id, layerName: normalLayer.name, id: generateId("redGreenImage"), }); // 清除普通图层原有内容 if (normalLayer.fabricObjects) { normalLayer.fabricObjects.forEach((obj) => { const { object } = findObjectById(this.canvas, obj.id); if (object) { this.canvas.remove(object); } }); } // 添加到画布和普通图层 this.canvas.add(img); normalLayer.fabricObjects = [img.toObject(["id", "type", "layerId"])]; this.redGreenImage = img; } /** * 设置普通图层透明度 */ _setupNormalLayerOpacity(normalLayers) { normalLayers.forEach((layer) => { // 设置图层透明度 layer.opacity = this.normalLayerOpacity; }); } /** * 设置工具管理器 */ _setupToolManager() { if (this.toolManager) { // 设置红绿图模式 this.toolManager.isRedGreenMode = true; // 切换到红色笔刷工具 this.toolManager.setTool(OperationType.RED_BRUSH); } } }