diff --git a/src/component/Canvas/CanvasEditor/managers/CanvasManager.js b/src/component/Canvas/CanvasEditor/managers/CanvasManager.js index 7ffdbe50..6d258eea 100644 --- a/src/component/Canvas/CanvasEditor/managers/CanvasManager.js +++ b/src/component/Canvas/CanvasEditor/managers/CanvasManager.js @@ -827,7 +827,7 @@ export class CanvasManager { restoreOpacityInRedGreen: options.restoreOpacityInRedGreen !== undefined ? options.restoreOpacityInRedGreen - : true, // 默认在红绿图模式下恢复透明度 + : false, // 默认在红绿图模式下恢复透明度 }; // 如果在红绿图模式下且没有指定具体的图层,自动包含所有普通图层 @@ -851,7 +851,6 @@ export class CanvasManager { console.log("红绿图模式导出图层:", normalLayerIds); } } - return await this.exportManager.exportImage(enhancedOptions); } catch (error) { console.error("CanvasManager导出图片失败:", error); diff --git a/src/component/Canvas/CanvasEditor/managers/ExportManager.js b/src/component/Canvas/CanvasEditor/managers/ExportManager.js index 0e6c21bd..6e472b5c 100644 --- a/src/component/Canvas/CanvasEditor/managers/ExportManager.js +++ b/src/component/Canvas/CanvasEditor/managers/ExportManager.js @@ -53,10 +53,10 @@ export class ExportManager { return this._exportMultipleLayers( layerIdArray, expPicType, + isContainBg, isContainFixed, isRedGreenMode, restoreOpacityInRedGreen, - isContainBg, isCropByBg ); } @@ -619,7 +619,7 @@ export class ExportManager { // this.canvasManager?.canvasHeight?.value || this.canvas.height; // console.log(`普通模式导出,画布尺寸: ${canvasWidth}x${canvasHeight}`); - + debugger; // 使用图层栅格化的方法导出图片 const dataURL = await createRasterizedImage({ canvas: this.canvas, @@ -629,6 +629,7 @@ export class ExportManager { maskObject: maskObject ?? null, // 使用裁剪对象 trimWhitespace: true, // 裁剪空白 trimPadding: 0, // 裁剪边距 + restoreOpacityInRedGreen, }); console.log("导出图片数据URL:", dataURL); diff --git a/src/component/Canvas/CanvasEditor/managers/RedGreenModeManager.js b/src/component/Canvas/CanvasEditor/managers/RedGreenModeManager.js index be27a519..b8496609 100644 --- a/src/component/Canvas/CanvasEditor/managers/RedGreenModeManager.js +++ b/src/component/Canvas/CanvasEditor/managers/RedGreenModeManager.js @@ -55,10 +55,14 @@ export class RedGreenModeManager { if (typeof options.normalLayerOpacity === "number") { if (options.normalLayerOpacity > 1) { // 如果大于1,认为是百分比值(0-100) - this.normalLayerOpacity = Math.max(0, Math.min(100, options.normalLayerOpacity)) / 100; + this.normalLayerOpacity = + Math.max(0, Math.min(100, options.normalLayerOpacity)) / 100; } else { // 如果小于等于1,认为是小数值(0-1) - this.normalLayerOpacity = Math.max(0, Math.min(1, options.normalLayerOpacity)); + this.normalLayerOpacity = Math.max( + 0, + Math.min(1, options.normalLayerOpacity) + ); } } @@ -93,7 +97,10 @@ export class RedGreenModeManager { this.registerRedGreenMouseUpEvent(); // 启用图层管理器的红绿图模式 - if (this.layerManager && typeof this.layerManager.enableRedGreenMode === "function") { + if ( + this.layerManager && + typeof this.layerManager.enableRedGreenMode === "function" + ) { this.layerManager.enableRedGreenMode(); } @@ -132,7 +139,9 @@ export class RedGreenModeManager { return; } if (this.onImageGenerated) { - const imageData = await this.canvasManager.exportImage(); + const imageData = await this.canvasManager.exportImage({ + restoreOpacityInRedGreen: true, // 恢复红绿图模式下的透明度 + }); this.onImageGenerated(imageData); } }); @@ -187,7 +196,9 @@ export class RedGreenModeManager { // 更新内部状态 this.normalLayerOpacity = normalizedOpacity; - console.log(`普通图层透明度已更新为: ${Math.round(normalizedOpacity * 100)}%`); + console.log( + `普通图层透明度已更新为: ${Math.round(normalizedOpacity * 100)}%` + ); return true; } catch (error) { console.error("更新普通图层透明度失败:", error); @@ -271,7 +282,9 @@ export class RedGreenModeManager { 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); + const normalLayers = layers.filter( + (layer) => !layer.isBackground && !layer.isFixed + ); return { backgroundLayer: @@ -307,7 +320,10 @@ export class RedGreenModeManager { cleanup() { try { // 禁用图层管理器的红绿图模式 - if (this.layerManager && typeof this.layerManager.disableRedGreenMode === "function") { + if ( + this.layerManager && + typeof this.layerManager.disableRedGreenMode === "function" + ) { this.layerManager.disableRedGreenMode(); } diff --git a/src/component/Canvas/CanvasEditor/utils/selectionToImage.js b/src/component/Canvas/CanvasEditor/utils/selectionToImage.js index b0b3b7f6..848f6916 100644 --- a/src/component/Canvas/CanvasEditor/utils/selectionToImage.js +++ b/src/component/Canvas/CanvasEditor/utils/selectionToImage.js @@ -20,6 +20,7 @@ export const createRasterizedImage = async ({ isReturenDataURL = false, // 是否返回DataURL而不是fabric.Image对象 preserveOriginalQuality = true, // 是否保持原始质量(新增) selectionManager = null, // 选区管理器,用于获取羽化值等设置 + restoreOpacityInRedGreen, // 是否在红绿图模式下恢复透明度 } = {}) => { try { console.log(`📊 开始栅格化 ${fabricObjects.length} 个对象`); @@ -85,12 +86,18 @@ const createClippedObjects = async ({ console.log("🎯 使用新的图像遮罩裁剪方法创建对象"); // 使用优化后的边界计算,确保包含描边区域 - const optimizedBounds = calculateOptimizedBounds(clippingObject, fabricObjects); + const optimizedBounds = calculateOptimizedBounds( + clippingObject, + fabricObjects + ); console.log("📐 优化后的选区边界框:", optimizedBounds); // 获取羽化值 let featherAmount = 0; - if (selectionManager && typeof selectionManager.getFeatherAmount === "function") { + if ( + selectionManager && + typeof selectionManager.getFeatherAmount === "function" + ) { featherAmount = selectionManager.getFeatherAmount(); console.log(`🌟 应用羽化效果: ${featherAmount}px`); } @@ -171,7 +178,10 @@ const createClippedDataURLByCanvas = async ({ console.log("🖼️ 使用图像遮罩裁剪方法生成DataURL"); // 使用优化后的边界计算,确保包含描边区域 - const optimizedBounds = calculateOptimizedBounds(clippingObject, fabricObjects); + const optimizedBounds = calculateOptimizedBounds( + clippingObject, + fabricObjects + ); // 使用高分辨率以保证质量 const pixelRatio = window.devicePixelRatio || 1; @@ -230,7 +240,13 @@ const createClippedDataURLByCanvas = async ({ * 创建简单克隆对象 * 当不需要裁剪时,直接克隆原对象 */ -const createSimpleClone = async ({ canvas, fabricObjects, isReturenDataURL, quality, format }) => { +const createSimpleClone = async ({ + canvas, + fabricObjects, + isReturenDataURL, + quality, + format, +}) => { try { console.log("📋 创建简单克隆对象"); @@ -444,7 +460,10 @@ const createLegacyRasterization = async ({ // 这里保留原有的离屏渲染逻辑作为备选方案 const currentZoom = canvas.getZoom?.() || 1; - scaleFactor = Math.max(scaleFactor || canvas?.getRetinaScaling?.(), currentZoom); + scaleFactor = Math.max( + scaleFactor || canvas?.getRetinaScaling?.(), + currentZoom + ); scaleFactor = Math.min(scaleFactor, 3); const { absoluteBounds, relativeBounds } = calculateBounds(fabricObjects); @@ -574,7 +593,9 @@ const createOffscreenRasterization = async ({ height: canvasHeight, }); - console.log(`🎨 离屏画布尺寸: ${canvasWidth}x${canvasHeight}, 缩放: ${scaleFactor}`); + console.log( + `🎨 离屏画布尺寸: ${canvasWidth}x${canvasHeight}, 缩放: ${scaleFactor}` + ); // 克隆对象到离屏画布 const clonedObjects = []; @@ -729,7 +750,11 @@ export const getObjectsBounds = (fabricObjects) => { * @param {Number} qualityMultiplier 质量倍数 * @returns {Promise} 遮罩图像的DataURL */ -const createMaskImageFromPath = async ({ clippingObject, selectionBounds, qualityMultiplier }) => { +const createMaskImageFromPath = async ({ + clippingObject, + selectionBounds, + qualityMultiplier, +}) => { try { console.log("🎭 创建路径遮罩图像"); @@ -745,7 +770,11 @@ const createMaskImageFromPath = async ({ clippingObject, selectionBounds, qualit }); // 克隆路径对象并处理描边转填充 - const maskPath = await createSolidMaskPath(clippingObject, selectionBounds, qualityMultiplier); + const maskPath = await createSolidMaskPath( + clippingObject, + selectionBounds, + qualityMultiplier + ); // 添加路径到遮罩画布 maskCanvas.add(maskPath); @@ -776,7 +805,11 @@ const createMaskImageFromPath = async ({ clippingObject, selectionBounds, qualit * @param {Number} qualityMultiplier 质量倍数 * @returns {Promise} 内容图像的DataURL */ -const renderContentToImage = async ({ fabricObjects, selectionBounds, qualityMultiplier }) => { +const renderContentToImage = async ({ + fabricObjects, + selectionBounds, + qualityMultiplier, +}) => { try { console.log("🖼️ 渲染内容图像"); @@ -808,8 +841,11 @@ const renderContentToImage = async ({ fabricObjects, selectionBounds, qualityMul // 如果有裁剪路径,也需要调整裁剪路径 if (clonedObj.clipPath) { clonedObj.clipPath.set({ - left: (clonedObj.clipPath.left - selectionBounds.left) * qualityMultiplier, - top: (clonedObj.clipPath.top - selectionBounds.top) * qualityMultiplier, + left: + (clonedObj.clipPath.left - selectionBounds.left) * + qualityMultiplier, + top: + (clonedObj.clipPath.top - selectionBounds.top) * qualityMultiplier, scaleX: (clonedObj.clipPath.scaleX || 1) * qualityMultiplier, scaleY: (clonedObj.clipPath.scaleY || 1) * qualityMultiplier, }); @@ -943,7 +979,11 @@ const createAdvancedMaskImage = async ({ }); // 克隆路径对象并处理描边转填充 - const maskPath = await createSolidMaskPath(clippingObject, selectionBounds, qualityMultiplier); + const maskPath = await createSolidMaskPath( + clippingObject, + selectionBounds, + qualityMultiplier + ); // 如果有羽化值,添加模糊效果 if (featherAmount > 0) { @@ -962,7 +1002,10 @@ const createAdvancedMaskImage = async ({ // 如果有羽化,需要进行后处理 if (featherAmount > 0) { - return await applyCanvasBlur(maskCanvas, featherAmount * qualityMultiplier); + return await applyCanvasBlur( + maskCanvas, + featherAmount * qualityMultiplier + ); } // 生成遮罩图像 @@ -1038,7 +1081,11 @@ const applyCanvasBlur = async (canvas, blurAmount) => { * @param {Number} qualityMultiplier 质量倍数 * @returns {Promise} 处理后的遮罩路径对象 */ -const createSolidMaskPath = async (clippingObject, selectionBounds, qualityMultiplier) => { +const createSolidMaskPath = async ( + clippingObject, + selectionBounds, + qualityMultiplier +) => { try { console.log("🔧 创建实体遮罩路径,处理描边转填充"); @@ -1049,19 +1096,29 @@ const createSolidMaskPath = async (clippingObject, selectionBounds, qualityMulti const hasStroke = maskPath.stroke && maskPath.strokeWidth > 0; if (hasStroke) { - console.log(`📏 检测到描边: ${maskPath.stroke}, 宽度: ${maskPath.strokeWidth}`); + console.log( + `📏 检测到描边: ${maskPath.stroke}, 宽度: ${maskPath.strokeWidth}` + ); // 对于有描边的路径,我们需要更精确的处理 const strokeWidth = maskPath.strokeWidth; // 方法1: 如果是简单的几何形状(矩形、圆形等),可以通过调整尺寸来补偿描边 - if (maskPath.type === "rect" || maskPath.type === "circle" || maskPath.type === "ellipse") { + if ( + maskPath.type === "rect" || + maskPath.type === "circle" || + maskPath.type === "ellipse" + ) { // 对于矩形和椭圆,增加宽高来包含描边 const strokeOffset = strokeWidth; maskPath.set({ - left: (maskPath.left - selectionBounds.left - strokeOffset / 2) * qualityMultiplier, - top: (maskPath.top - selectionBounds.top - strokeOffset / 2) * qualityMultiplier, + left: + (maskPath.left - selectionBounds.left - strokeOffset / 2) * + qualityMultiplier, + top: + (maskPath.top - selectionBounds.top - strokeOffset / 2) * + qualityMultiplier, scaleX: (maskPath.scaleX || 1) * qualityMultiplier, scaleY: (maskPath.scaleY || 1) * qualityMultiplier, width: (maskPath.width || 0) + strokeOffset, @@ -1080,8 +1137,12 @@ const createSolidMaskPath = async (clippingObject, selectionBounds, qualityMulti const strokeOffset = strokeWidth / 2; maskPath.set({ - left: (maskPath.left - selectionBounds.left - strokeOffset) * qualityMultiplier, - top: (maskPath.top - selectionBounds.top - strokeOffset) * qualityMultiplier, + left: + (maskPath.left - selectionBounds.left - strokeOffset) * + qualityMultiplier, + top: + (maskPath.top - selectionBounds.top - strokeOffset) * + qualityMultiplier, scaleX: (maskPath.scaleX || 1) * qualityMultiplier * expandRatio, scaleY: (maskPath.scaleY || 1) * qualityMultiplier * expandRatio, fill: "#ffffff",