import { fabric } from "fabric-with-all"; import { findObjectById } from "../utils/helper"; import { createRasterizedImage } from "../utils/selectionToImage"; import { OperationType, SpecialLayerId } from "../utils/layerHelper"; /** * 图片导出管理器 * 负责处理画布的图片导出功能,支持多种导出选项和图层过滤 */ export class ExportManager { constructor(canvasManager, layerManager) { this.canvasManager = canvasManager; this.layerManager = layerManager; this.canvas = canvasManager.canvas; } /** * 导出图片 * @param {Object} options 导出选项 * @param {Boolean} options.isContainBg 是否包含背景图层 * @param {Boolean} options.isContainFixed 是否包含固定图层 * @param {Boolean} options.isContainFixedOther 是否包含其他固定图层 * @param {Boolean} options.isContainNormalLayer 是否包含普通图层 * @param {Boolean} options.isCropByBg 是否使用背景大小裁剪 * @param {String} options.layerId 导出具体图层ID * @param {Array} options.layerIdArray 导出多个图层ID数组 * @param {Array} options.layerIdArray2 导出多个图层ID数组2 * @param {String} options.expPicType 导出图片类型 (png/jpg/svg) * @param {Boolean} options.restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1 * @param {Boolean} options.isEnhanceImg 是否是增强图片 * @param {Array} options.excludedLayers 排除的图层ID数组 * @returns {String} 导出的图片数据URL */ async exportImage(options = {}) { const { isContainBg = false, isContainFixed = false, isContainFixedOther = false, // 是否包含其他固定图层 isContainNormalLayer = true, // 是否包含普通图层 isCropByBg = false, // 是否使用背景大小裁剪 layerId = "", layerIdArray = [], layerIdArray2 = null, expPicType = "png", restoreOpacityInRedGreen = true, isEnhanceImg, // 是否是增强图片 excludedLayers = [], // 排除的图层ID数组 } = options; try { // 检查是否为红绿图模式 const isRedGreenMode = this.layerManager?.isInRedGreenMode?.() || false; // 如果指定了具体图层ID,导出指定图层 if (layerId) { return this._exportSpecificLayer( layerId, expPicType, isRedGreenMode, restoreOpacityInRedGreen, isCropByBg, isEnhanceImg, // 是否是增强图片 ); } // 如果指定了多个图层ID,导出多个图层 if (layerIdArray && layerIdArray.length > 0) { return this._exportMultipleLayers( layerIdArray, expPicType, isContainBg, isContainFixed, isContainFixedOther, // 是否包含其他固定图层 isContainNormalLayer, // 是否包含普通图层 isRedGreenMode, restoreOpacityInRedGreen, isCropByBg, isEnhanceImg, // 是否是增强图片 ); } // 默认导出所有可见图层 return this._exportAllLayers( expPicType, isContainBg, isContainFixed, isContainFixedOther, // 是否包含其他固定图层 isContainNormalLayer, // 是否包含普通图层 isRedGreenMode, restoreOpacityInRedGreen, isCropByBg, isEnhanceImg, // 是否是增强图片 layerIdArray2, excludedLayers, // 排除的图层ID数组 ); } catch (error) { console.error("导出图片失败:", error); throw new Error(`图片导出失败: ${error.message}`); } } /** * 导出指定单个图层 * @param {String} layerId 图层ID * @param {String} expPicType 导出类型 * @param {Boolean} isRedGreenMode 是否为红绿图模式 * @param {Boolean} restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1 * @param {Boolean} isCropByBg 是否使用背景大小裁剪 * @param {Boolean} isEnhanceImg 是否是增强图片 * @returns {String} 图片数据URL * @private */ async _exportSpecificLayer( layerId, expPicType, isRedGreenMode, restoreOpacityInRedGreen, isCropByBg, // 是否使用背景大小裁剪 isEnhanceImg, // 是否是增强图片 ) { if (!this.layerManager) { throw new Error("图层管理器未初始化"); } const layer = this._getLayerById(layerId); if (!layer) { throw new Error(`未找到ID为 ${layerId} 的图层`); } if (!layer.visible) { console.warn(`图层 ${layer.name} 不可见,将导出空白图片`); } // 收集所有需要导出的对象 const objectsToExport = this._collectObjectsFromLayer(layer); if (objectsToExport.length === 0) { console.warn(`图层 ${layer.name} 没有可导出的对象`); return this._generateEmptyImage(expPicType); } // 红绿图模式下使用固定尺寸和裁剪 if (isRedGreenMode) { return this._exportWithRedGreenMode( objectsToExport, expPicType, restoreOpacityInRedGreen, ); } // 普通模式使用画布尺寸 return await this._exportWithCanvasSize( objectsToExport, expPicType, restoreOpacityInRedGreen, isCropByBg, // 是否使用背景大小裁剪 isEnhanceImg, // 是否是增强图片 ); } /** * 导出多个指定图层 * @param {Array} layerIdArray 图层ID数组 * @param {String} expPicType 导出类型 * @param {Boolean} isContainBg 是否包含背景图层 * @param {Boolean} isContainFixed 是否包含固定图层 * @param {Boolean} isContainFixedOther 是否包含其他固定图层 * @param {Boolean} isContainNormalLayer 是否包含普通图层 * @param {Boolean} isRedGreenMode 是否为红绿图模式 * @param {Boolean} restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1 * @param {Boolean} isCropByBg 是否使用背景大小裁剪 * @param {Boolean} isEnhanceImg 是否是增强图片 * @returns {String} 图片数据URL * @private */ async _exportMultipleLayers( layerIdArray, expPicType, isContainBg, isContainFixed, isContainFixedOther, // 是否包含其他固定图层 isContainNormalLayer = true, // 是否包含普通图层 isRedGreenMode, restoreOpacityInRedGreen, isCropByBg, // 是否使用背景大小裁剪 isEnhanceImg, // 是否是增强图片 ) { if (!this.layerManager) { throw new Error("图层管理器未初始化"); } // 按图层顺序收集对象(从底到顶) const objectsToExport = this._collectObjectsByLayerOrder( layerIdArray, isContainBg, isContainFixed, isContainFixedOther, // 是否包含其他固定图层 isContainNormalLayer, // 是否包含普通图层 ); if (objectsToExport.length === 0) { console.warn("没有可导出的对象"); return this._generateEmptyImage(expPicType); } // 红绿图模式下使用固定尺寸和裁剪 if (isRedGreenMode) { return this._exportWithRedGreenMode( objectsToExport, expPicType, restoreOpacityInRedGreen ); } // 普通模式使用画布尺寸 return await this._exportWithCanvasSize( objectsToExport, expPicType, restoreOpacityInRedGreen, isCropByBg, // 是否使用背景大小裁剪 isEnhanceImg, // 是否是增强图片 ); } /** * 导出所有图层 * @param {String} expPicType 导出类型 * @param {Boolean} isContainBg 是否包含背景图层 * @param {Boolean} isContainFixed 是否包含固定图层 * @param {Boolean} isContainFixedOther 是否包含其他固定图层 * @param {Boolean} isContainNormalLayer 是否包含普通图层 * @param {Boolean} isRedGreenMode 是否为红绿图模式 * @param {Boolean} restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1 * @param {Boolean} isCropByBg 是否使用背景大小裁剪 * @param {Boolean} isEnhanceImg 是否是增强图片 * @param {Array} layerIdArray 导出多个图层ID数组2 * @returns {String} 图片数据URL * @private */ async _exportAllLayers( expPicType, isContainBg, isContainFixed, isContainFixedOther, // 是否包含其他固定图层 isContainNormalLayer, // 是否包含普通图层 isRedGreenMode, restoreOpacityInRedGreen, isCropByBg, // 是否使用背景大小裁剪 isEnhanceImg, // 是否是增强图片 layerIdArray, // 导出所有图层 excludedLayers, // 排除的图层ID数组 ) { // 按图层顺序收集对象(从底到顶) const objectsToExport = this._collectObjectsByLayerOrder( layerIdArray, // 导出所有图层 isContainBg, isContainFixed, isContainFixedOther, // 是否包含其他固定图层 isContainNormalLayer, // 是否包含普通图层 excludedLayers, ); if (objectsToExport.length === 0) { console.warn("没有可导出的对象"); return this._generateEmptyImage(expPicType); } // 红绿图模式下使用固定尺寸和裁剪 if (isRedGreenMode) { return this._exportWithRedGreenMode( objectsToExport, expPicType, restoreOpacityInRedGreen ); } let canvasClipPath = this.canvas.clipPath; if (isCropByBg) { const cropWidth = this.canvasManager?.canvasWidth?.value || this.canvas?.canvasWidth || this.canvas.width; const cropHeight = this.canvasManager?.canvasHeight?.value || this.canvas?.canvasHeight || this.canvas.height; canvasClipPath = new fabric.Rect({ left: this.canvas.width / 2, top: this.canvas.height / 2, width: cropWidth, height: cropHeight, originX: "center", originY: "center", fill: "#fff", stroke: "transparent", strokeWidth: 0, }); canvasClipPath.set({ absolutePositioned: true, }); canvasClipPath.setCoords(); } // 普通模式使用画布尺寸 return await this._exportWithCanvasSize( objectsToExport, expPicType, restoreOpacityInRedGreen, canvasClipPath, isCropByBg, // 是否使用背景大小裁剪 isEnhanceImg, // 是否是增强图片 ); } /** * 从图层收集对象(优化版本 - 通过ID查找画布中的真实对象) * @param {Object} layer 图层对象 * @param {Boolean} isChildren 是否递归收集子图层的对象 * @returns {Array} 画布中的真实对象数组 * @private */ _collectObjectsFromLayer(layer, isChildren = true) { if (!layer) { return []; } const realObjects = []; // 收集当前图层的对象 if (layer.fabricObjects && layer.fabricObjects.length > 0) { for (const layerObj of layer.fabricObjects) { if (!layerObj || !layerObj.id) continue; // 通过ID在画布中查找真实对象 const realObj = this._findRealObjectById(layerObj.id); if (realObj && realObj.visible !== false) { realObjects.push(realObj); } } } if (layer.fabricObject) { // 通过ID在画布中查找真实对象 const realObj = this._findRealObjectById(layer.fabricObject.id); if (realObj && realObj.visible !== false) { realObjects.push(realObj); } } // 递归收集子图层的对象 if (isChildren && layer.children && layer.children.length > 0) { for (let i = layer.children.length - 1; i >= 0; i--) { const childLayer = layer.children[i]; const childObjects = this._collectObjectsFromLayer(childLayer, isChildren); realObjects.push(...childObjects); } } return realObjects; } /** * 通过ID在画布中查找真实对象 * @param {String} objectId 对象ID * @returns {Object|null} 画布中的真实对象 * @private */ _findRealObjectById(objectId) { if (!objectId || !this.canvas) { return null; } try { // 使用helper工具查找对象 const result = findObjectById(this.canvas, objectId); return result?.object || null; } catch (error) { console.warn(`查找对象 ${objectId} 失败:`, error); return null; } } /** * 导出对象 * @param {Object} obj fabric对象 * @param {String} expPicType 导出类型 * @param {Boolean} isRedGreenMode 是否为红绿图模式 * @param {Boolean} restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1 * @returns {String} 图片数据URL * @private */ async _exportObject( obj, expPicType, isRedGreenMode, restoreOpacityInRedGreen ) { // 红绿图模式下使用固定尺寸和裁剪 if (isRedGreenMode) { return this._exportWithRedGreenMode( [obj], expPicType, restoreOpacityInRedGreen ); } // 普通模式使用画布尺寸 return await this._exportWithCanvasSize( [obj], expPicType, restoreOpacityInRedGreen ); } /** * 按图层顺序收集对象(优化版本 - 从底到顶) * @param {Array|null} layerIdArray 图层ID数组,null表示所有图层 * @param {Boolean} isContainBg 是否包含背景图层 * @param {Boolean} isContainFixed 是否包含固定图层 * @param {Boolean} isContainFixedOther 是否包含其他固定图层 * @param {Boolean} isContainNormalLayer 是否包含普通图层 * @param {Array} excludedLayers 排除的图层ID数组 * @returns {Array} 按正确顺序排列的真实对象数组 * @private */ _collectObjectsByLayerOrder(layerIdArray, isContainBg, isContainFixed, isContainFixedOther, isContainNormalLayer, excludedLayers) { const objectsToExport = []; const allLayers = this._getAllLayersFlattened(excludedLayers); // 获取扁平化的图层列表 // 图层数组是从顶到底的顺序,需要反向遍历以获得从底到顶的渲染顺序 for (let i = allLayers.length - 1; i >= 0; i--) { const layer = allLayers[i]; // 如果指定了图层ID数组,只处理指定的图层 if (layerIdArray && !layerIdArray.includes(layer.id)) continue; // 检查图层类型过滤条件 if (!this._shouldIncludeLayer(layer, isContainBg, isContainFixed, isContainFixedOther, isContainNormalLayer)) continue; if (layer.visible) { const layerObjects = this._collectObjectsFromLayer(layer, false); objectsToExport.push(...layerObjects); } } return objectsToExport; } /** * 获取扁平化的图层列表(包含子图层),排除指定的图层 * @param {Array} excludedLayers 排除的图层ID数组 * @returns {Array} 扁平化的图层数组 * @private */ _getAllLayersFlattened(excludedLayers) { const flattenedLayers = []; const rootLayers = this._getAllLayers(); const flattenLayer = (layer) => { // 检查是否在排除列表中 if (excludedLayers && excludedLayers.includes(layer.id)) return; flattenedLayers.push(layer); // 递归处理子图层 if (layer.children && layer.children.length > 0) { for (const childLayer of layer.children) { flattenLayer(childLayer); } } }; // 处理所有根图层 for (const layer of rootLayers) { flattenLayer(layer); } return flattenedLayers; } /** * 计算对象组的边界 * @param {Array} objects 对象数组 * @returns {Object} 边界信息 {left, top, width, height} * @private */ _calculateGroupBounds(objects) { if (!objects || objects.length === 0) { return { left: 0, top: 0, width: 1, height: 1 }; } let minX = Infinity; let minY = Infinity; let maxX = -Infinity; let maxY = -Infinity; objects.forEach((obj) => { if (!obj || typeof obj.getBoundingRect !== "function") { return; } const bounds = obj.getBoundingRect(); minX = Math.min(minX, bounds.left); minY = Math.min(minY, bounds.top); maxX = Math.max(maxX, bounds.left + bounds.width); maxY = Math.max(maxY, bounds.top + bounds.height); }); if (minX === Infinity || minY === Infinity) { return { left: 0, top: 0, width: 1, height: 1 }; } // 添加小量边距避免边缘裁切 const padding = 2; return { left: minX - padding, top: minY - padding, width: maxX - minX + padding * 2, height: maxY - minY + padding * 2, }; } /** * 克隆对象并添加到临时画布,调整位置偏移 * @param {fabric.Canvas} tempCanvas 临时画布 * @param {Object} obj 要克隆的对象 * @param {Object} bounds 边界信息 * @param {Boolean} isRedGreenMode 是否为红绿图模式 * @param {Boolean} restoreOpacityInRedGreen 是否恢复透明度 * @returns {Promise} 克隆的对象 * @private */ async _cloneAndAddObjectWithOffset( tempCanvas, obj, bounds, isRedGreenMode, restoreOpacityInRedGreen ) { try { const cloned = await this._cloneObjectForExport( obj, isRedGreenMode && restoreOpacityInRedGreen ); if (cloned) { // 获取对象当前边界 const objBounds = obj.getBoundingRect(); // 计算相对于组边界的偏移 const offsetX = objBounds.left - bounds.left; const offsetY = objBounds.top - bounds.top; // 设置新位置(相对于临时画布的原点) cloned.set({ left: offsetX + objBounds.width / 2, top: offsetY + objBounds.height / 2, originX: "center", originY: "center", }); cloned.setCoords(); tempCanvas.add(cloned); return cloned; } } catch (error) { console.warn(`克隆对象失败: ${obj?.id || "未知"}`, error); } return null; } /** * 红绿图模式导出(使用固定图层底图作为画布尺寸和裁剪区域) * @param {Array} objectsToExport 要导出的对象数组 * @param {String} expPicType 导出类型 * @param {Boolean} restoreOpacityInRedGreen 是否恢复透明度为1 * @returns {String} 图片数据URL * @private */ async _exportWithRedGreenMode( objectsToExport, expPicType, restoreOpacityInRedGreen ) { // 获取固定图层对象(衣服底图)作为参考 const fixedLayerObject = this._getFixedLayerObject() ?? this.canvas.clipPath; if (!fixedLayerObject) { console.warn("红绿图模式下未找到固定图层对象,使用画布尺寸"); return await this._exportWithCanvasSize( objectsToExport, expPicType, restoreOpacityInRedGreen, ); } // 使用固定图层的实际显示尺寸作为导出画布尺寸 const canvasWidth = (fixedLayerObject.width); const canvasHeight = (fixedLayerObject.height); console.log(`红绿图模式导出,画布尺寸: ${canvasWidth}x${canvasHeight}`); const tempFabricCanvas = new fabric.StaticCanvas() tempFabricCanvas.setDimensions({ width: canvasWidth, height: canvasHeight, backgroundColor: null, // enableRetinaScaling: true, imageSmoothingEnabled: true, }); // tempFabricCanvas.setZoom(1); const ox = fixedLayerObject.left - fixedLayerObject.width * fixedLayerObject.scaleX / 2 const oy = fixedLayerObject.top - fixedLayerObject.height * fixedLayerObject.scaleY / 2 // console.log("==========", fixedLayerObject, ox, oy) try { // 克隆并添加所有对象到临时画布,需要调整位置相对于固定图层 for (let i = 0; i < objectsToExport.length; i++) { const obj = objectsToExport[i]; const cloned = await this._cloneObjectForExport( obj, restoreOpacityInRedGreen && true ); if (cloned) { let scaleX = cloned.scaleX / fixedLayerObject.scaleX let scaleY = cloned.scaleY / fixedLayerObject.scaleY let top = (cloned.top - oy) * scaleY let left = (cloned.left - ox) * scaleX if (cloned.originX === "center" && cloned.originY === "center") { top = canvasHeight / 2 left = canvasWidth / 2 } cloned.set({ left: left, top: top, scaleX: scaleX, scaleY: scaleY, }); // 更新对象坐标 cloned.setCoords(); tempFabricCanvas.add(cloned); } } // 渲染画布 tempFabricCanvas.renderAll(); // 生成图片 return this._generateHighQualityDataURL(tempFabricCanvas, expPicType); } finally { this._cleanupTempCanvas(tempFabricCanvas); } } /** * 普通模式导出(使用画布尺寸) * @param {Array} objectsToExport 要导出的对象数组 * @param {String} expPicType 导出类型 * @param {Boolean} restoreOpacityInRedGreen 是否恢复透明度为1 * @param {Object} maskObject 裁剪对象 * @param {Boolean} isCropByBg 是否使用背景大小裁剪 * @param {Boolean} isEnhanceImg 是否是增强图片 * @returns {String} 图片数据URL * @private */ async _exportWithCanvasSize( objectsToExport, expPicType, restoreOpacityInRedGreen, maskObject, // 裁剪对象 isCropByBg, // 是否使用背景大小裁剪 isEnhanceImg, // 是否是增强图片 ) { // 使用当前画布尺寸 // const canvasWidth = // this.canvasManager?.canvasWidth?.value || this.canvas.width; // const canvasHeight = // this.canvasManager?.canvasHeight?.value || this.canvas.height; // console.log(`普通模式导出,画布尺寸: ${canvasWidth}x${canvasHeight}`); // 使用图层栅格化的方法导出图片 const dataURL = await createRasterizedImage({ canvas: this.canvas, fabricObjects: objectsToExport, format: expPicType, // 导出格式 isReturenDataURL: true, // 返回数据URL maskObject: maskObject ?? null, // 使用裁剪对象 trimWhitespace: true, // 裁剪空白 trimPadding: 0, // 裁剪边距 restoreOpacityInRedGreen, isCropByBg, // 是否使用背景大小裁剪 isEnhanceImg, // 是否是增强图片 }); // console.log("导出图片数据URL:", dataURL); return dataURL; // // 创建与画布相同尺寸的临时画布 // const scaleFactor = 2; // 高清导出 // const tempCanvas = document.createElement("canvas"); // tempCanvas.width = canvasWidth * scaleFactor; // tempCanvas.height = canvasHeight * scaleFactor; // tempCanvas.style.width = canvasWidth + "px"; // tempCanvas.style.height = canvasHeight + "px"; // const tempFabricCanvas = new fabric.StaticCanvas(tempCanvas, { // width: canvasWidth, // height: canvasHeight, // backgroundColor: null, // }); // tempFabricCanvas.enableRetinaScaling = true; // tempFabricCanvas.imageSmoothingEnabled = true; // tempFabricCanvas.setZoom(1); // try { // // 克隆并添加所有对象到临时画布 // for (const obj of objectsToExport) { // const cloned = await this._cloneObjectForExport( // obj, // restoreOpacityInRedGreen && false, // 普通模式不强制恢复透明度 // ); // if (cloned) { // tempFabricCanvas.add(cloned); // } // } // // 渲染画布 // tempFabricCanvas.renderAll(); // // 生成图片 // return this._generateHighQualityDataURL(tempCanvas, expPicType); // } finally { // this._cleanupTempCanvas(tempFabricCanvas); // } } /** * 获取固定图层对象 * @returns {Object|null} 固定图层对象 * @private */ _getFixedLayerObject() { const allLayers = this._getAllLayers(); const fixedLayer = allLayers.find((layer) => layer.isFixed); if (!fixedLayer || !fixedLayer.fabricObject) { return null; } // 如果有ID,通过ID查找画布中的实际对象 if (fixedLayer.fabricObject.id) { const result = findObjectById(this.canvas, fixedLayer.fabricObject.id); return result.object || fixedLayer.fabricObject; } return fixedLayer.fabricObject; } /** * 异步克隆fabric对象(参照createRasterizedImage的方法) * @param {fabric.Object} obj 要克隆的对象 * @param {Array} propertiesToInclude 要包含的属性 * @returns {Promise} 克隆的对象 * @private */ _cloneObjectAsync( obj, propertiesToInclude = ["id", "layerId", "layerName", "name", "scaleX", "scaleY"] ) { return new Promise((resolve, reject) => { if (!obj) { resolve(null); return; } try { obj.clone((cloned) => { if (cloned) { resolve(cloned); } else { reject(new Error("对象克隆失败")); } }, propertiesToInclude); } catch (error) { console.warn("克隆对象失败:", error); resolve(null); } }); } /** * 克隆对象用于导出(优化版本) * @param {Object} obj fabric对象 * @param {Boolean} forceRestoreOpacity 是否强制恢复透明度为1 * @param {Boolean} removeClipPath 是否移除裁剪路径 * @returns {Promise} 克隆的对象 * @private */ async _cloneObjectForExport( obj, forceRestoreOpacity = false, removeClipPath = true ) { if (!obj) return null; try { // 使用异步克隆方法 const cloned = await this._cloneObjectAsync(obj); if (cloned) { // 保持原始位置和属性 cloned.set({ selectable: false, evented: false, visible: true, }); // 如果需要恢复透明度 if (forceRestoreOpacity) { cloned.set({ opacity: 1 }); } // 移除裁剪路径以避免绝对路径问题 if (removeClipPath && cloned.clipPath) { console.log(`移除对象 ${cloned.id || "未知"} 的裁剪路径`); cloned.clipPath = null; } return cloned; } } catch (error) { console.warn("克隆对象失败:", error); } return null; } /** * 导出对象组 * @param {Array} objectsToExport 要导出的对象数组 * @param {String} expPicType 导出类型 * @param {Boolean} isRedGreenMode 是否为红绿图模式 * @param {Boolean} restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1 * @returns {Promise} 图片数据URL * @private */ async _exportObjectsAsGroup( objectsToExport, expPicType, isRedGreenMode = false, restoreOpacityInRedGreen = true ) { if (!objectsToExport || objectsToExport.length === 0) { throw new Error("没有可导出的对象"); } // 计算所有对象的边界 const bounds = this._calculateGroupBounds(objectsToExport); console.log("导出边界:", bounds); // 创建高质量临时画布 const scaleFactor = 2; // 高清导出 const tempCanvas = document.createElement("canvas"); tempCanvas.width = bounds.width * scaleFactor; tempCanvas.height = bounds.height * scaleFactor; tempCanvas.style.width = bounds.width + "px"; tempCanvas.style.height = bounds.height + "px"; const tempFabricCanvas = new fabric.StaticCanvas(tempCanvas, { width: bounds.width, height: bounds.height, backgroundColor: null, // 透明背景 }); // 启用高清缩放和图像平滑 tempFabricCanvas.enableRetinaScaling = true; tempFabricCanvas.imageSmoothingEnabled = true; tempFabricCanvas.setZoom(scaleFactor); try { // 克隆所有对象并添加到临时画布 const clonedObjects = []; for (const obj of objectsToExport) { const cloned = await this._cloneAndAddObjectWithOffset( tempFabricCanvas, obj, bounds, isRedGreenMode, restoreOpacityInRedGreen ); if (cloned) { clonedObjects.push(cloned); } } console.log(`成功克隆 ${clonedObjects.length} 个对象进行导出`); // 渲染画布 tempFabricCanvas.renderAll(); // 生成高质量数据URL return this._generateHighQualityDataURL(tempCanvas, expPicType); } finally { this._cleanupTempCanvas(tempFabricCanvas); } } /** * 获取裁剪路径对象(优化版本) * @param {Object} fixedBounds 固定图层边界 * @returns {Promise} 裁剪路径对象 * @private */ async _getClipPathObject(fixedBounds) { try { // const allLayers = this._getAllLayers(); // // 查找第一个有裁剪遮罩的图层 // let clipObject = null; // for (const layer of allLayers) { // if (layer.clippingMask?.id) { // const result = findObjectById(this.canvas, layer.clippingMask.id); // if (result?.object) { // clipObject = result.object; // break; // } // } // } const clipObject = this.canvas?.clipPath; if (!clipObject) { console.warn("未找到可用的裁剪对象"); return null; } // 克隆对象作为裁剪路径 const clonedClipPath = await this._cloneObjectForExport( clipObject, false, false ); if (!clonedClipPath) { console.warn("无法克隆裁剪对象"); return null; } // 调整裁剪路径的位置相对于固定图层 clonedClipPath.set({ left: clonedClipPath.left - fixedBounds.left, top: clonedClipPath.top - fixedBounds.top, absolutePositioned: true, // 使用绝对定位 }); // 更新坐标 clonedClipPath.setCoords(); console.log("成功创建裁剪路径:", { objectType: clonedClipPath.type, position: { left: clonedClipPath.left, top: clonedClipPath.top }, size: { width: clonedClipPath.width, height: clonedClipPath.height }, }); return clonedClipPath; } catch (error) { console.error("获取裁剪路径失败:", error); return null; } } /** * 生成高质量数据URL * @param {HTMLCanvasElement} canvas 画布元素 * @param {String} expPicType 导出类型 * @returns {String} 数据URL * @private */ _generateHighQualityDataURL(canvas, expPicType) { const format = expPicType.toLowerCase(); switch (format) { case "jpg": case "jpeg": // 对于JPEG,使用较高质量,但JPEG不支持透明背景 return canvas.toDataURL("image/jpeg", 0.95); case "svg": // SVG导出需要特殊处理,这里先返回高质量PNG console.warn("SVG导出暂未实现,返回高质量PNG格式"); return canvas.toDataURL("image/png", 1.0); case "png": default: // PNG使用最高质量,支持透明背景 return canvas.toDataURL("image/png", 1.0); } } /** * 生成空白图片 * @param {String} expPicType 导出类型 * @returns {String} 空白图片数据URL * @private */ _generateEmptyImage(expPicType) { const emptyCanvas = document.createElement("canvas"); emptyCanvas.width = 1; emptyCanvas.height = 1; // 确保透明背景 const ctx = emptyCanvas.getContext("2d"); ctx.clearRect(0, 0, 1, 1); return this._generateHighQualityDataURL(emptyCanvas, expPicType); } /** * 清理临时画布资源 * @param {fabric.StaticCanvas} tempFabricCanvas 临时Fabric画布 * @private */ _cleanupTempCanvas(tempFabricCanvas) { if (tempFabricCanvas) { try { tempFabricCanvas.dispose(); } catch (error) { console.warn("清理临时画布失败:", error); } } } /** * 获取所有图层 * @returns {Array} 图层数组 * @private */ _getAllLayers() { if (this.layerManager && this.layerManager.layers) { return this.layerManager.layers.value || []; } return []; } /** * 根据ID获取图层 * @param {String} layerId 图层ID * @returns {Object|null} 图层对象 * @private */ _getLayerById(layerId) { if (this.layerManager && this.layerManager.getLayerById) { return this.layerManager.getLayerById(layerId); } // 备用方法:直接从图层数组中查找 const allLayers = this._getAllLayers(); return allLayers.find((layer) => layer.id === layerId) || null; } /** * 检查图层是否应该包含在导出中 * @param {Object} layer 图层对象 * @param {Boolean} isContainBg 是否包含背景图层 * @param {Boolean} isContainFixed 是否包含固定图层 * @param {Boolean} isContainFixedOther 是否包含其他固定图层 * @param {Boolean} isContainNormalLayer 是否包含普通图层 * @returns {Boolean} 是否应该包含 * @private */ _shouldIncludeLayer(layer, isContainBg, isContainFixed, isContainFixedOther, isContainNormalLayer) { if (!layer) return false; // 检查背景图层 if (layer.isBackground) { return isContainBg; } // 检查固定图层 if (layer.isFixed) { return isContainFixed; } // 检查其他固定图层 if (layer.isFixedOther) { return isContainFixedOther; } // 印花图层始终导出 if (layer.isPrintTrims || layer.isPrintTrimsGroup) { return true; } // 普通图层 return isContainNormalLayer; } }