/** * 图片导出管理器 * 负责处理画布的图片导出功能,支持多种导出选项和图层过滤 */ 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 {String} options.layerId 导出具体图层ID * @param {Array} options.layerIdArray 导出多个图层ID数组 * @param {String} options.expPicType 导出图片类型 (png/jpg/svg) * @returns {String} 导出的图片数据URL */ exportImage(options = {}) { const { isContainBg = false, isContainFixed = false, layerId = "", layerIdArray = [], expPicType = "png" } = options; try { // 如果指定了具体图层ID,导出指定图层 if (layerId) { return this._exportSpecificLayer(layerId, expPicType); } // 如果指定了多个图层ID,导出多个图层 if (layerIdArray && layerIdArray.length > 0) { return this._exportMultipleLayers(layerIdArray, expPicType, isContainBg, isContainFixed); } // 默认导出所有可见图层 return this._exportAllLayers(expPicType, isContainBg, isContainFixed); } catch (error) { console.error("导出图片失败:", error); throw new Error(`图片导出失败: ${error.message}`); } } /** * 导出指定单个图层 * @param {String} layerId 图层ID * @param {String} expPicType 导出类型 * @returns {String} 图片数据URL * @private */ _exportSpecificLayer(layerId, expPicType) { 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 tempCanvas = this._createExportCanvas(); const tempFabricCanvas = this._createTempFabricCanvas(tempCanvas); try { // 只添加指定图层的对象 this._addLayerObjectsToCanvas(tempFabricCanvas, layer); // 渲染并导出 tempFabricCanvas.renderAll(); return this._generateDataURL(tempCanvas, expPicType); } finally { this._cleanupTempCanvas(tempFabricCanvas); } } /** * 导出多个指定图层 * @param {Array} layerIdArray 图层ID数组 * @param {String} expPicType 导出类型 * @param {Boolean} isContainBg 是否包含背景图层 * @param {Boolean} isContainFixed 是否包含固定图层 * @returns {String} 图片数据URL * @private */ _exportMultipleLayers(layerIdArray, expPicType, isContainBg, isContainFixed) { if (!this.layerManager) { throw new Error("图层管理器未初始化"); } // 创建临时画布 const tempCanvas = this._createExportCanvas(); const tempFabricCanvas = this._createTempFabricCanvas(tempCanvas); try { // 按照图层顺序添加指定的图层 const allLayers = this._getAllLayers(); allLayers.forEach(layer => { if (!layerIdArray.includes(layer.id)) return; // 检查图层类型过滤条件 if (!this._shouldIncludeLayer(layer, isContainBg, isContainFixed)) return; if (layer.visible) { this._addLayerObjectsToCanvas(tempFabricCanvas, layer); } }); // 渲染并导出 tempFabricCanvas.renderAll(); return this._generateDataURL(tempCanvas, expPicType); } finally { this._cleanupTempCanvas(tempFabricCanvas); } } /** * 导出所有图层 * @param {String} expPicType 导出类型 * @param {Boolean} isContainBg 是否包含背景图层 * @param {Boolean} isContainFixed 是否包含固定图层 * @returns {String} 图片数据URL * @private */ _exportAllLayers(expPicType, isContainBg, isContainFixed) { // 创建临时画布 const tempCanvas = this._createExportCanvas(); const tempFabricCanvas = this._createTempFabricCanvas(tempCanvas); try { // 获取所有图层并按顺序添加 const allLayers = this._getAllLayers(); allLayers.forEach(layer => { // 检查图层类型过滤条件 if (!this._shouldIncludeLayer(layer, isContainBg, isContainFixed)) return; if (layer.visible) { this._addLayerObjectsToCanvas(tempFabricCanvas, layer); } }); // 渲染并导出 tempFabricCanvas.renderAll(); return this._generateDataURL(tempCanvas, expPicType); } finally { this._cleanupTempCanvas(tempFabricCanvas); } } /** * 判断是否应该包含该图层 * @param {Object} layer 图层对象 * @param {Boolean} isContainBg 是否包含背景图层 * @param {Boolean} isContainFixed 是否包含固定图层 * @returns {Boolean} 是否包含 * @private */ _shouldIncludeLayer(layer, isContainBg, isContainFixed) { // 背景图层处理 if (layer.type === 'background' || layer.isBackground) { return isContainBg; } // 固定图层处理 if (layer.type === 'fixed' || layer.isFixed || layer.locked) { return isContainFixed; } // 其他图层默认包含 return true; } /** * 创建导出用的临时画布 * @returns {HTMLCanvasElement} 临时画布 * @private */ _createExportCanvas() { const tempCanvas = document.createElement("canvas"); tempCanvas.width = this.canvas.width || 800; tempCanvas.height = this.canvas.height || 600; return tempCanvas; } /** * 创建临时Fabric画布 * @param {HTMLCanvasElement} tempCanvas 临时画布元素 * @returns {fabric.StaticCanvas} 临时Fabric画布 * @private */ _createTempFabricCanvas(tempCanvas) { const { fabric } = window; const tempFabricCanvas = new fabric.StaticCanvas(tempCanvas, { width: this.canvas.width || 800, height: this.canvas.height || 600, backgroundColor: this.canvas.backgroundColor || 'transparent' }); // 设置高质量渲染选项 tempFabricCanvas.enableRetinaScaling = true; tempFabricCanvas.imageSmoothingEnabled = true; return tempFabricCanvas; } /** * 将图层对象添加到临时画布 * @param {fabric.StaticCanvas} tempCanvas 临时画布 * @param {Object} layer 图层对象 * @private */ _addLayerObjectsToCanvas(tempCanvas, layer) { if (!layer) return; // 处理背景图层 if (layer.type === 'background' && layer.fabricObject) { this._cloneAndAddObject(tempCanvas, layer.fabricObject); return; } // 处理普通图层的对象 if (layer.fabricObjects && Array.isArray(layer.fabricObjects)) { layer.fabricObjects.forEach(obj => { if (obj && obj.visible !== false) { this._cloneAndAddObject(tempCanvas, obj); } }); } // 处理单个fabricObject的情况 if (layer.fabricObject && !layer.fabricObjects) { this._cloneAndAddObject(tempCanvas, layer.fabricObject); } // 处理分组图层的子图层 if (layer.children && Array.isArray(layer.children)) { layer.children.forEach(childLayerId => { const childLayer = this._getLayerById(childLayerId); if (childLayer && childLayer.visible) { this._addLayerObjectsToCanvas(tempCanvas, childLayer); } }); } } /** * 克隆并添加对象到临时画布 * @param {fabric.StaticCanvas} tempCanvas 临时画布 * @param {fabric.Object} obj Fabric对象 * @private */ _cloneAndAddObject(tempCanvas, obj) { if (!obj) return; try { obj.clone((cloned) => { if (cloned) { // 确保克隆对象的属性正确 cloned.set({ selectable: false, evented: false, visible: true }); tempCanvas.add(cloned); } }, ['id', 'layerId', 'name']); // 保留自定义属性 } catch (error) { console.warn("克隆对象失败:", error); } } /** * 生成数据URL * @param {HTMLCanvasElement} canvas 画布元素 * @param {String} expPicType 导出类型 * @returns {String} 数据URL * @private */ _generateDataURL(canvas, expPicType) { const format = expPicType.toLowerCase(); switch (format) { case 'jpg': case 'jpeg': return canvas.toDataURL('image/jpeg', 0.9); case 'svg': // SVG导出需要特殊处理,这里先返回PNG console.warn("SVG导出暂未实现,返回PNG格式"); return canvas.toDataURL('image/png', 1.0); case 'png': default: return canvas.toDataURL('image/png', 1.0); } } /** * 清理临时画布资源 * @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; } }