/** * 生产环境矢量图形渲染管理器 * 统一管理所有矢量图形的渲染策略和错误处理 */ import { VECTOR_EXPORT_CONFIG } from '@/config/vectorExportConfig'; import { svg2Base64 } from './svg2Base64'; import { renderElementToBase64, getElementDimensions } from './canvasRenderer'; // 渲染策略枚举 export enum RenderStrategy { SVG_SERIALIZATION = 'svg_serialization', CANVAS_RENDER = 'canvas_render', SIMPLIFIED_SVG = 'simplified_svg', PLACEHOLDER = 'placeholder' } // 渲染结果接口 export interface RenderResult { success: boolean; data?: string; strategy: RenderStrategy; duration: number; error?: Error; metadata?: { width: number; height: number; format: string; size: number; }; } // 性能监控器 class PerformanceMonitor { private static instance: PerformanceMonitor; private metrics: Map = new Map(); static getInstance(): PerformanceMonitor { if (!this.instance) { this.instance = new PerformanceMonitor(); } return this.instance; } recordMetric(operation: string, duration: number): void { if (!VECTOR_EXPORT_CONFIG.PERFORMANCE_CONFIG.MONITORING.ENABLED) return; if (!this.metrics.has(operation)) { this.metrics.set(operation, []); } const metrics = this.metrics.get(operation)!; metrics.push(duration); // 保持最近的记录数量 const maxCount = VECTOR_EXPORT_CONFIG.PERFORMANCE_CONFIG.MONITORING.MAX_METRICS_COUNT; if (metrics.length > maxCount) { metrics.shift(); } } getAverageTime(operation: string): number { const metrics = this.metrics.get(operation); if (!metrics || metrics.length === 0) return 0; return metrics.reduce((sum, time) => sum + time, 0) / metrics.length; } getMetrics(): Record { const result: Record = {}; for (const [operation, times] of this.metrics.entries()) { if (times.length > 0) { result[operation] = { average: this.getAverageTime(operation), count: times.length, latest: times[times.length - 1] }; } } return result; } } // 错误处理器 class ErrorHandler { static logError(error: Error, context: string, element?: Element): void { const config = VECTOR_EXPORT_CONFIG.ERROR_CONFIG.LOGGING; if (config.ERROR_ONLY && VECTOR_EXPORT_CONFIG.ENVIRONMENT.isProduction()) { console.error(`VectorRenderManager[${context}]: ${error.message}`); } else if (config.VERBOSE) { console.error(`VectorRenderManager[${context}]:`, { error: error.message, stack: config.INCLUDE_STACK_TRACE ? error.stack : undefined, element: element ? { tagName: element.tagName, className: element.className, id: element.id } : undefined }); } } static async retry( operation: () => Promise, context: string, maxAttempts: number = VECTOR_EXPORT_CONFIG.ERROR_CONFIG.RETRY.MAX_ATTEMPTS ): Promise { let lastError: Error; for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { return await operation(); } catch (error) { lastError = error as Error; if (attempt === maxAttempts) { this.logError(lastError, `${context} (final attempt ${attempt}/${maxAttempts})`); throw lastError; } const delay = VECTOR_EXPORT_CONFIG.ERROR_CONFIG.RETRY.DELAY_MS * Math.pow(VECTOR_EXPORT_CONFIG.ERROR_CONFIG.RETRY.BACKOFF_MULTIPLIER, attempt - 1); this.logError(lastError, `${context} (attempt ${attempt}/${maxAttempts}, retrying in ${delay}ms)`); await new Promise(resolve => setTimeout(resolve, delay)); } } throw lastError!; } } // 主渲染管理器 export class VectorRenderManager { private static instance: VectorRenderManager; private monitor: PerformanceMonitor; private constructor() { this.monitor = PerformanceMonitor.getInstance(); } static getInstance(): VectorRenderManager { if (!this.instance) { this.instance = new VectorRenderManager(); } return this.instance; } /** * 渲染元素为Base64图像 */ async renderElement(element: Element, preferredStrategy?: RenderStrategy): Promise { const startTime = performance.now(); // 验证元素 const validation = this.validateElement(element); if (!validation.valid) { return { success: false, strategy: RenderStrategy.PLACEHOLDER, duration: performance.now() - startTime, error: new Error(validation.reason || 'Element validation failed') }; } // 确定渲染策略顺序 const strategies = this.getStrategies(element, preferredStrategy); // 尝试每个策略 for (const strategy of strategies) { try { const result = await this.executeStrategy(element, strategy); if (result.success) { const duration = performance.now() - startTime; this.monitor.recordMetric(`render_success_${strategy}`, duration); return { ...result, duration }; } } catch (error) { ErrorHandler.logError(error as Error, `Strategy ${strategy}`, element); continue; } } // 所有策略都失败,返回占位符 const duration = performance.now() - startTime; this.monitor.recordMetric('render_fallback_placeholder', duration); return this.createPlaceholder(element, duration); } /** * 验证元素是否可以渲染 */ private validateElement(element: Element): { valid: boolean; reason?: string } { if (!element) { return { valid: false, reason: 'Element is null or undefined' }; } // 检查元素尺寸 const dimensions = getElementDimensions(element as HTMLElement); const config = VECTOR_EXPORT_CONFIG.VALIDATION_CONFIG.DIMENSIONS; if (!dimensions.isValid) { return { valid: false, reason: 'Element has invalid dimensions' }; } if (dimensions.width < config.MIN_WIDTH || dimensions.height < config.MIN_HEIGHT) { return { valid: false, reason: 'Element dimensions too small' }; } if (dimensions.width > config.MAX_WIDTH || dimensions.height > config.MAX_HEIGHT) { return { valid: false, reason: 'Element dimensions too large' }; } return { valid: true }; } /** * 获取渲染策略顺序 */ private getStrategies(element: Element, preferred?: RenderStrategy): RenderStrategy[] { const isSVG = element.tagName.toLowerCase() === 'svg'; const isHuggingface = VECTOR_EXPORT_CONFIG.ENVIRONMENT.isHuggingface(); const canvasSupported = VECTOR_EXPORT_CONFIG.COMPATIBILITY_CONFIG.FEATURES.CANVAS_SUPPORT(); let strategies: RenderStrategy[] = []; // 如果指定了首选策略,优先使用 if (preferred) { strategies.push(preferred); } // 根据环境和元素类型确定策略顺序 if (isSVG) { if (!strategies.includes(RenderStrategy.SVG_SERIALIZATION)) { strategies.push(RenderStrategy.SVG_SERIALIZATION); } if (!strategies.includes(RenderStrategy.SIMPLIFIED_SVG)) { strategies.push(RenderStrategy.SIMPLIFIED_SVG); } } // Canvas渲染(如果支持且不是Huggingface环境) if (canvasSupported && !isHuggingface && !strategies.includes(RenderStrategy.CANVAS_RENDER)) { strategies.push(RenderStrategy.CANVAS_RENDER); } // 最后的占位符策略 if (!strategies.includes(RenderStrategy.PLACEHOLDER)) { strategies.push(RenderStrategy.PLACEHOLDER); } return strategies; } /** * 执行特定的渲染策略 */ private async executeStrategy(element: Element, strategy: RenderStrategy): Promise { const startTime = performance.now(); switch (strategy) { case RenderStrategy.SVG_SERIALIZATION: return this.executeSVGSerialization(element, startTime); case RenderStrategy.CANVAS_RENDER: return this.executeCanvasRender(element, startTime); case RenderStrategy.SIMPLIFIED_SVG: return this.executeSimplifiedSVG(element, startTime); case RenderStrategy.PLACEHOLDER: return this.createPlaceholder(element, performance.now() - startTime); default: throw new Error(`Unknown strategy: ${strategy}`); } } /** * SVG序列化策略 */ private async executeSVGSerialization(element: Element, startTime: number): Promise { try { const base64 = svg2Base64(element); const duration = performance.now() - startTime; return { success: true, data: base64, strategy: RenderStrategy.SVG_SERIALIZATION, duration, metadata: { width: element.getBoundingClientRect().width, height: element.getBoundingClientRect().height, format: 'svg', size: base64.length } }; } catch (error) { return { success: false, strategy: RenderStrategy.SVG_SERIALIZATION, duration: performance.now() - startTime, error: error as Error }; } } /** * Canvas渲染策略 */ private async executeCanvasRender(element: Element, startTime: number): Promise { try { const base64 = await renderElementToBase64(element as HTMLElement, { scale: VECTOR_EXPORT_CONFIG.PERFORMANCE_CONFIG.CANVAS.DEFAULT_SCALE, backgroundColor: VECTOR_EXPORT_CONFIG.PERFORMANCE_CONFIG.CANVAS.BACKGROUND_COLOR, timeout: VECTOR_EXPORT_CONFIG.PERFORMANCE_CONFIG.RENDER_TIMEOUT }); const duration = performance.now() - startTime; return { success: true, data: base64, strategy: RenderStrategy.CANVAS_RENDER, duration, metadata: { width: element.getBoundingClientRect().width, height: element.getBoundingClientRect().height, format: 'png', size: base64.length } }; } catch (error) { return { success: false, strategy: RenderStrategy.CANVAS_RENDER, duration: performance.now() - startTime, error: error as Error }; } } /** * 简化SVG策略 */ private async executeSimplifiedSVG(element: Element, startTime: number): Promise { try { const rect = element.getBoundingClientRect(); const width = rect.width || 100; const height = rect.height || 100; // 创建简化的SVG const simplifiedSvg = ` SVG `; const base64 = `data:image/svg+xml;base64,${btoa(simplifiedSvg)}`; const duration = performance.now() - startTime; return { success: true, data: base64, strategy: RenderStrategy.SIMPLIFIED_SVG, duration, metadata: { width, height, format: 'svg', size: base64.length } }; } catch (error) { return { success: false, strategy: RenderStrategy.SIMPLIFIED_SVG, duration: performance.now() - startTime, error: error as Error }; } } /** * 创建占位符 */ private createPlaceholder(element: Element, duration: number): RenderResult { const rect = element.getBoundingClientRect(); const width = rect.width || 100; const height = rect.height || 100; const config = VECTOR_EXPORT_CONFIG.ERROR_CONFIG.FALLBACK; const placeholderSvg = ` ${config.PLACEHOLDER_TEXT} `; const base64 = `data:image/svg+xml;base64,${btoa(placeholderSvg)}`; return { success: true, data: base64, strategy: RenderStrategy.PLACEHOLDER, duration, metadata: { width, height, format: 'svg', size: base64.length } }; } /** * 获取性能指标 */ getPerformanceMetrics(): Record { return this.monitor.getMetrics(); } } // 导出单例实例 export const vectorRenderManager = VectorRenderManager.getInstance(); export default vectorRenderManager;