customer-chat-sdk 1.0.33 → 1.0.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,7 +13,7 @@ export interface ScreenshotOptions {
13
13
  enableCORS?: boolean;
14
14
  proxyUrl?: string;
15
15
  useProxy?: boolean;
16
- engine?: 'modern-screenshot' | 'snapdom';
16
+ engine?: 'modern-screenshot' | 'snapdom' | 'html2canvas';
17
17
  corsMode?: 'simple' | 'smart' | 'proxy' | 'blob' | 'canvas-proxy' | 'ignore' | 'test-first';
18
18
  silentMode?: boolean;
19
19
  maxRetries?: number;
@@ -28,6 +28,8 @@ export interface ScreenshotOptions {
28
28
  fetchPriority?: 'high' | 'low' | 'auto';
29
29
  maxCacheSize?: number;
30
30
  maxCacheAge?: number;
31
+ maxImageSize?: number;
32
+ skipLargeImages?: boolean;
31
33
  }
32
34
  /**
33
35
  * 上传配置接口
@@ -69,6 +71,9 @@ export declare class ScreenshotManager {
69
71
  private intersectionObserver;
70
72
  private visibleElementsCache;
71
73
  private preconnected;
74
+ private imageDownloadQueue;
75
+ private activeDownloads;
76
+ private maxConcurrentImageDownloads;
72
77
  private globalErrorHandler;
73
78
  private globalRejectionHandler;
74
79
  constructor(targetElement: HTMLElement | null, options?: ScreenshotOptions);
@@ -120,8 +125,41 @@ export declare class ScreenshotManager {
120
125
  * 注意:snapdom 内部使用 worker 进行截图处理,会在后台线程执行,不会阻塞主线程
121
126
  */
122
127
  private takeScreenshotWithSnapdom;
128
+ /**
129
+ * 使用 html2canvas 截图
130
+ *
131
+ * 优势:
132
+ * - 处理 SVG 和本地资源更快(不需要复杂的 Worker 通信)
133
+ * - 兼容性好,支持更多 CSS 特性
134
+ * - 跨域处理相对简单
135
+ *
136
+ * 劣势:
137
+ * - 在主线程执行,可能阻塞 UI(但处理速度快,影响较小)
138
+ * - 不支持 Worker 模式
139
+ *
140
+ * 适用场景:
141
+ * - 页面包含大量 SVG 图标
142
+ * - 本地资源较多
143
+ * - 需要快速截图
144
+ */
145
+ private takeScreenshotWithHtml2Canvas;
123
146
  /**
124
147
  * 使用 modern-screenshot 截图(启用 Worker)
148
+ *
149
+ * 优势:
150
+ * - 使用 Worker,不阻塞主线程 UI
151
+ * - 支持并发处理
152
+ * - 适合复杂页面
153
+ *
154
+ * 劣势:
155
+ * - 处理 SVG 和本地资源较慢(Worker 通信开销)
156
+ * - 配置相对复杂
157
+ * - 需要处理 Worker URL
158
+ *
159
+ * 适用场景:
160
+ * - 复杂页面,需要不阻塞 UI
161
+ * - 需要高质量截图
162
+ * - 页面资源较少
125
163
  */
126
164
  private takeScreenshotWithModernScreenshot;
127
165
  /**
@@ -176,6 +214,10 @@ export declare class ScreenshotManager {
176
214
  * 通过代理服务器获取图片并转换为 data URL
177
215
  */
178
216
  private proxyImage;
217
+ /**
218
+ * 不使用代理时下载图片(带内存保护)
219
+ */
220
+ private downloadImageWithoutProxy;
179
221
  /**
180
222
  * 将 blob 转换为 data URL
181
223
  */
@@ -1 +1 @@
1
- {"version":3,"file":"ScreenshotManager.d.ts","sourceRoot":"","sources":["../../src/core/ScreenshotManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ScreenshotManager.d.ts","sourceRoot":"","sources":["../../src/core/ScreenshotManager.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,8 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
+ var html2canvas = require('html2canvas');
6
+
5
7
  // 直接使用base64字符串,避免打包后路径问题
6
8
  const iconImage = 'data:image/png;base64,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';
7
9
  class IconManager {
@@ -6507,13 +6509,17 @@ class ScreenshotManager {
6507
6509
  this.visibleElementsCache = new Set();
6508
6510
  // 预连接状态
6509
6511
  this.preconnected = false;
6512
+ // 图片下载队列和并发控制(防止频繁截图时重复下载)
6513
+ this.imageDownloadQueue = new Map(); // URL -> Promise,避免重复下载
6514
+ this.activeDownloads = new Set(); // 正在下载的 URL
6515
+ this.maxConcurrentImageDownloads = 5; // 最大并发下载数(降低,避免内存问题)
6510
6516
  // 全局错误处理器
6511
6517
  this.globalErrorHandler = null;
6512
6518
  this.globalRejectionHandler = null;
6513
6519
  this.targetElement = targetElement;
6514
6520
  this.options = {
6515
6521
  interval: options.interval ?? 5000,
6516
- quality: options.quality ?? 0.4,
6522
+ quality: options.quality ?? 0.3, // 降低默认质量:0.4 -> 0.3,减少 base64 大小
6517
6523
  scale: options.scale ?? 1,
6518
6524
  maxHistory: options.maxHistory ?? 10,
6519
6525
  compress: options.compress ?? false,
@@ -6537,7 +6543,9 @@ class ScreenshotManager {
6537
6543
  useIntersectionObserver: options.useIntersectionObserver ?? true, // 默认使用 Intersection Observer
6538
6544
  fetchPriority: options.fetchPriority ?? 'high', // 默认高优先级
6539
6545
  maxCacheSize: options.maxCacheSize ?? 50, // 默认最大50MB
6540
- maxCacheAge: options.maxCacheAge ?? 86400000 // 默认24小时(86400000ms)
6546
+ maxCacheAge: options.maxCacheAge ?? 86400000, // 默认24小时(86400000ms)
6547
+ maxImageSize: options.maxImageSize ?? 5, // 不使用代理时,单个图片最大尺寸(MB),默认5MB
6548
+ skipLargeImages: options.skipLargeImages ?? true // 不使用代理时,是否跳过过大的图片,默认true(跳过)
6541
6549
  };
6542
6550
  this.setupMessageListener();
6543
6551
  this.setupVisibilityChangeListener();
@@ -6898,6 +6906,9 @@ class ScreenshotManager {
6898
6906
  if (selectedEngine === 'snapdom') {
6899
6907
  dataUrl = await this.takeScreenshotWithSnapdom(this.targetElement);
6900
6908
  }
6909
+ else if (selectedEngine === 'html2canvas') {
6910
+ dataUrl = await this.takeScreenshotWithHtml2Canvas(this.targetElement);
6911
+ }
6901
6912
  else {
6902
6913
  // 默认使用 modern-screenshot
6903
6914
  dataUrl = await this.takeScreenshotWithModernScreenshot(this.targetElement);
@@ -7060,8 +7071,173 @@ class ScreenshotManager {
7060
7071
  throw error;
7061
7072
  }
7062
7073
  }
7074
+ /**
7075
+ * 使用 html2canvas 截图
7076
+ *
7077
+ * 优势:
7078
+ * - 处理 SVG 和本地资源更快(不需要复杂的 Worker 通信)
7079
+ * - 兼容性好,支持更多 CSS 特性
7080
+ * - 跨域处理相对简单
7081
+ *
7082
+ * 劣势:
7083
+ * - 在主线程执行,可能阻塞 UI(但处理速度快,影响较小)
7084
+ * - 不支持 Worker 模式
7085
+ *
7086
+ * 适用场景:
7087
+ * - 页面包含大量 SVG 图标
7088
+ * - 本地资源较多
7089
+ * - 需要快速截图
7090
+ */
7091
+ async takeScreenshotWithHtml2Canvas(element) {
7092
+ if (!this.options.silentMode) {
7093
+ console.log('📸 使用 html2canvas 引擎截图...');
7094
+ }
7095
+ try {
7096
+ // 检查元素是否存在和可见
7097
+ const rect = element.getBoundingClientRect();
7098
+ if (rect.width === 0 || rect.height === 0) {
7099
+ throw new Error('元素尺寸为 0,无法截图');
7100
+ }
7101
+ const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
7102
+ const isLowEndDevice = navigator.hardwareConcurrency && navigator.hardwareConcurrency <= 4;
7103
+ // 计算压缩后的尺寸
7104
+ let elementWidth = element.scrollWidth || element.clientWidth || element.offsetWidth;
7105
+ let elementHeight = element.scrollHeight || element.clientHeight || element.offsetHeight;
7106
+ if (element === document.body || element === document.documentElement) {
7107
+ elementWidth = Math.max(element.scrollWidth, element.offsetWidth, document.documentElement.scrollWidth, document.documentElement.offsetWidth, window.innerWidth);
7108
+ elementHeight = Math.max(element.scrollHeight, element.offsetHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight, window.innerHeight);
7109
+ }
7110
+ const { width, height } = this.calculateCompressedSize(elementWidth, elementHeight, this.options.maxWidth, this.options.maxHeight);
7111
+ const finalWidth = width < elementWidth ? width : Math.min(elementWidth, this.options.maxWidth);
7112
+ const finalHeight = height < elementHeight ? height : Math.min(elementHeight, this.options.maxHeight);
7113
+ // html2canvas 质量设置(0-1)
7114
+ const finalQuality = isMobile || isLowEndDevice
7115
+ ? Math.max(this.options.quality * 0.65, 0.2)
7116
+ : this.options.quality;
7117
+ // html2canvas 配置选项
7118
+ const options = {
7119
+ // 基本配置
7120
+ backgroundColor: '#ffffff',
7121
+ scale: this.options.scale !== 1 ? (isMobile ? 0.7 : this.options.scale) : (isMobile ? 0.7 : 1),
7122
+ useCORS: this.options.enableCORS,
7123
+ allowTaint: !this.options.enableCORS, // 如果启用 CORS,不允许 taint
7124
+ logging: !this.options.silentMode,
7125
+ width: finalWidth,
7126
+ height: finalHeight,
7127
+ // 性能优化
7128
+ removeContainer: true, // 截图后移除临时容器
7129
+ imageTimeout: this.options.imageLoadTimeout || 5000,
7130
+ // 忽略某些元素(可选,提升性能)
7131
+ ignoreElements: (element) => {
7132
+ // 忽略隐藏元素
7133
+ const style = window.getComputedStyle(element);
7134
+ if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') {
7135
+ return true;
7136
+ }
7137
+ return false;
7138
+ },
7139
+ };
7140
+ // html2canvas 不支持直接的 proxy 选项,需要通过 onclone 钩子处理图片
7141
+ // 如果配置了代理服务器,在克隆时替换图片 URL
7142
+ if (this.options.useProxy && this.options.proxyUrl && this.options.proxyUrl.trim() !== '') {
7143
+ options.onclone = (clonedDoc) => {
7144
+ // 在克隆的文档中,替换所有跨域图片的 src
7145
+ const images = clonedDoc.querySelectorAll('img');
7146
+ images.forEach((img) => {
7147
+ const originalSrc = img.getAttribute('src');
7148
+ if (!originalSrc)
7149
+ return;
7150
+ // 检查是否是跨域图片
7151
+ try {
7152
+ const imgUrl = new URL(originalSrc, window.location.href);
7153
+ if (imgUrl.origin === window.location.origin) {
7154
+ return; // 同源图片,不需要处理
7155
+ }
7156
+ }
7157
+ catch {
7158
+ // URL 解析失败,可能是相对路径,继续处理
7159
+ }
7160
+ // 检查缓存
7161
+ const cachedDataUrl = this.getCachedImage(originalSrc);
7162
+ if (cachedDataUrl) {
7163
+ img.src = cachedDataUrl;
7164
+ return;
7165
+ }
7166
+ // 对于跨域图片,使用代理 URL
7167
+ // html2canvas 会自动处理,但我们可以预先处理
7168
+ // 注意:html2canvas 会自己处理图片加载,这里主要是为了缓存
7169
+ });
7170
+ };
7171
+ }
7172
+ if (!this.options.silentMode) {
7173
+ console.log(`📸 html2canvas 配置: 尺寸 ${finalWidth}x${finalHeight}, 质量 ${finalQuality.toFixed(2)}, 缩放 ${options.scale}`);
7174
+ }
7175
+ // 执行截图
7176
+ const canvas = await html2canvas(element, options);
7177
+ // 根据输出格式转换
7178
+ let mimeType = 'image/png';
7179
+ let finalQualityForExport = undefined;
7180
+ if (this.options.outputFormat === 'webp' && !isMobile) {
7181
+ try {
7182
+ const testCanvas = document.createElement('canvas');
7183
+ testCanvas.width = 1;
7184
+ testCanvas.height = 1;
7185
+ const testDataUrl = testCanvas.toDataURL('image/webp');
7186
+ if (testDataUrl.indexOf('webp') !== -1) {
7187
+ mimeType = 'image/webp';
7188
+ finalQualityForExport = finalQuality;
7189
+ }
7190
+ }
7191
+ catch {
7192
+ mimeType = 'image/jpeg';
7193
+ finalQualityForExport = finalQuality;
7194
+ }
7195
+ }
7196
+ else if (this.options.outputFormat === 'jpeg') {
7197
+ mimeType = 'image/jpeg';
7198
+ finalQualityForExport = finalQuality;
7199
+ }
7200
+ // 转换为 data URL
7201
+ const dataUrl = mimeType === 'image/png'
7202
+ ? canvas.toDataURL(mimeType)
7203
+ : canvas.toDataURL(mimeType, finalQualityForExport);
7204
+ // 验证结果
7205
+ if (!dataUrl || dataUrl.length < 100) {
7206
+ throw new Error('生成的截图数据无效或过短');
7207
+ }
7208
+ if (!this.options.silentMode) {
7209
+ console.log(`📸 html2canvas 截图成功!格式: ${this.options.outputFormat}, 尺寸: ${canvas.width}x${canvas.height}`);
7210
+ }
7211
+ return dataUrl;
7212
+ }
7213
+ catch (error) {
7214
+ const errorMessage = error instanceof Error ? error.message : String(error);
7215
+ if (!this.options.silentMode) {
7216
+ console.error('📸 html2canvas 截图失败:', errorMessage);
7217
+ if (errorMessage.includes('CORS') || errorMessage.includes('cross-origin')) {
7218
+ console.warn('📸 💡 建议:配置 proxyUrl 选项处理跨域图片');
7219
+ }
7220
+ }
7221
+ throw error;
7222
+ }
7223
+ }
7063
7224
  /**
7064
7225
  * 使用 modern-screenshot 截图(启用 Worker)
7226
+ *
7227
+ * 优势:
7228
+ * - 使用 Worker,不阻塞主线程 UI
7229
+ * - 支持并发处理
7230
+ * - 适合复杂页面
7231
+ *
7232
+ * 劣势:
7233
+ * - 处理 SVG 和本地资源较慢(Worker 通信开销)
7234
+ * - 配置相对复杂
7235
+ * - 需要处理 Worker URL
7236
+ *
7237
+ * 适用场景:
7238
+ * - 复杂页面,需要不阻塞 UI
7239
+ * - 需要高质量截图
7240
+ * - 页面资源较少
7065
7241
  */
7066
7242
  async takeScreenshotWithModernScreenshot(element) {
7067
7243
  if (!this.options.silentMode) {
@@ -7094,13 +7270,19 @@ class ScreenshotManager {
7094
7270
  }
7095
7271
  const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
7096
7272
  const isLowEndDevice = navigator.hardwareConcurrency && navigator.hardwareConcurrency <= 4;
7097
- const mobileQuality = isMobile || isLowEndDevice ? Math.max(this.options.quality * 0.7, 0.3) : this.options.quality;
7098
- // 不限制宽度和高度,让 modern-screenshot 自动处理完整内容
7099
- // 只在需要压缩时才应用尺寸限制
7273
+ // 进一步降低质量以减少 base64 大小
7274
+ // 桌面设备:使用配置的质量(默认 0.3)
7275
+ // 移动设备/低端设备:进一步降低到 0.2(最低)
7276
+ const finalQuality = isMobile || isLowEndDevice
7277
+ ? Math.max(this.options.quality * 0.65, 0.2) // 移动设备:质量 * 0.65,最低 0.2
7278
+ : this.options.quality; // 桌面设备:使用配置的质量(默认 0.3)
7279
+ // 计算压缩后的尺寸(对所有元素都应用,包括 document.body)
7280
+ // 这样可以避免生成过大的截图,减少 base64 大小
7100
7281
  const { width, height } = this.calculateCompressedSize(elementWidth, elementHeight, this.options.maxWidth, this.options.maxHeight);
7101
- // 如果计算后的尺寸小于元素实际尺寸,说明需要压缩,否则使用元素实际尺寸
7102
- const finalWidth = width < elementWidth ? width : undefined;
7103
- const finalHeight = height < elementHeight ? height : undefined;
7282
+ // 对于所有元素都应用尺寸限制(包括 body),避免截图过大
7283
+ // 如果计算后的尺寸小于元素实际尺寸,使用压缩尺寸;否则使用元素实际尺寸(但不超过最大值)
7284
+ const finalWidth = width < elementWidth ? width : Math.min(elementWidth, this.options.maxWidth);
7285
+ const finalHeight = height < elementHeight ? height : Math.min(elementHeight, this.options.maxHeight);
7104
7286
  // 处理跨域图片的函数
7105
7287
  const handleCrossOriginImage = async (url) => {
7106
7288
  // 如果是 data URL 或 blob URL,直接返回
@@ -7199,9 +7381,64 @@ class ScreenshotManager {
7199
7381
  return url;
7200
7382
  }
7201
7383
  }
7202
- // 如果没有配置代理,尝试使用 CORS
7384
+ // 如果没有配置代理,需要添加内存保护机制
7385
+ // 不使用代理时,modern-screenshot 会直接下载图片,可能导致内存问题
7386
+ // 由于已配置 CORS,可以直接下载并检查大小
7203
7387
  if (this.options.enableCORS) {
7204
- return url;
7388
+ // 对于不使用代理的情况,添加内存保护和缓存机制:
7389
+ // 1. 先检查内存缓存(避免重复下载)
7390
+ // 2. 检查 IndexedDB 缓存
7391
+ // 3. 使用下载队列避免并发重复下载
7392
+ // 4. 下载时检查大小,如果过大则使用占位符
7393
+ // 先检查内存缓存(优先使用缓存,避免重复下载)
7394
+ const cachedDataUrl = this.getCachedImage(url);
7395
+ if (cachedDataUrl) {
7396
+ if (!this.options.silentMode) {
7397
+ console.log(`📸 ✅ 使用内存缓存图片(无代理模式): ${url.substring(0, 50)}...`);
7398
+ }
7399
+ return cachedDataUrl;
7400
+ }
7401
+ // 检查 IndexedDB 缓存(如果启用)
7402
+ if (this.options.useIndexedDB) {
7403
+ const indexedDBCache = await this.getIndexedDBCache(url);
7404
+ if (indexedDBCache) {
7405
+ // 同步到内存缓存
7406
+ this.setCachedImage(url, indexedDBCache);
7407
+ if (!this.options.silentMode) {
7408
+ console.log(`📸 ✅ 使用 IndexedDB 缓存图片(无代理模式): ${url.substring(0, 50)}...`);
7409
+ }
7410
+ return indexedDBCache;
7411
+ }
7412
+ }
7413
+ // 检查是否正在下载(避免重复下载)
7414
+ if (this.imageDownloadQueue.has(url)) {
7415
+ // 如果已经在下载队列中,等待现有下载完成
7416
+ if (!this.options.silentMode) {
7417
+ console.log(`📸 ⏳ 等待图片下载完成: ${url.substring(0, 50)}...`);
7418
+ }
7419
+ return await this.imageDownloadQueue.get(url);
7420
+ }
7421
+ // 检查并发下载数限制
7422
+ if (this.activeDownloads.size >= this.maxConcurrentImageDownloads) {
7423
+ // 并发数已满,返回原 URL,让 modern-screenshot 自己处理(可能会失败,但不阻塞)
7424
+ if (!this.options.silentMode) {
7425
+ console.warn(`📸 ⚠️ 并发下载数已满(${this.activeDownloads.size}/${this.maxConcurrentImageDownloads}),跳过: ${url.substring(0, 50)}...`);
7426
+ }
7427
+ return url;
7428
+ }
7429
+ // 创建下载 Promise 并加入队列
7430
+ const downloadPromise = this.downloadImageWithoutProxy(url);
7431
+ this.imageDownloadQueue.set(url, downloadPromise);
7432
+ this.activeDownloads.add(url);
7433
+ try {
7434
+ const result = await downloadPromise;
7435
+ return result;
7436
+ }
7437
+ finally {
7438
+ // 下载完成后清理
7439
+ this.imageDownloadQueue.delete(url);
7440
+ this.activeDownloads.delete(url);
7441
+ }
7205
7442
  }
7206
7443
  // 默认返回原 URL
7207
7444
  return url;
@@ -7222,11 +7459,14 @@ class ScreenshotManager {
7222
7459
  }
7223
7460
  this.screenshotContext = null;
7224
7461
  }
7462
+ // Worker 数量配置:移动设备/低端设备使用 1 个 Worker,桌面设备使用 2 个
7463
+ // workerNumber > 0 会启用 Worker 模式,截图处理在后台线程执行,不会阻塞主线程 UI
7225
7464
  const workerNumber = isMobile || isLowEndDevice ? 1 : 2;
7226
7465
  // 构建 createContext 配置
7466
+ // 参考: https://github.com/qq15725/modern-screenshot/blob/main/src/options.ts
7227
7467
  const contextOptions = {
7228
- workerNumber,
7229
- quality: mobileQuality,
7468
+ workerNumber, // Worker 数量,> 0 启用 Worker 模式
7469
+ quality: finalQuality, // 图片质量(0-1),已优化为更低的值以减少 base64 大小
7230
7470
  fetchFn: handleCrossOriginImage, // 使用代理服务器处理跨域图片
7231
7471
  fetch: {
7232
7472
  requestInit: {
@@ -7234,35 +7474,45 @@ class ScreenshotManager {
7234
7474
  },
7235
7475
  bypassingCache: true,
7236
7476
  },
7477
+ // 设置最大 canvas 尺寸,防止生成过大的 canvas(避免内存问题)
7478
+ // 参考: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas#maximum_canvas_size
7479
+ // 大多数浏览器限制为 16,777,216 像素(4096x4096),这里设置为更保守的值
7480
+ maximumCanvasSize: 16777216, // 16M 像素(约 4096x4096)
7237
7481
  };
7238
- // 对于 document.body,不设置 width/height,让 modern-screenshot 自动处理完整页面
7239
- // 只有在需要压缩且不是 body 元素时才指定尺寸
7240
- if (element !== document.body && element !== document.documentElement) {
7241
- if (finalWidth && finalHeight && (finalWidth < elementWidth || finalHeight < elementHeight)) {
7242
- contextOptions.width = finalWidth;
7243
- contextOptions.height = finalHeight;
7244
- if (!this.options.silentMode) {
7245
- console.log(`📸 使用压缩尺寸: ${finalWidth}x${finalHeight}`);
7482
+ // 对所有元素都设置尺寸限制(包括 document.body),避免截图过大
7483
+ // 这样可以减少 base64 大小,提高性能
7484
+ if (finalWidth && finalHeight) {
7485
+ contextOptions.width = finalWidth;
7486
+ contextOptions.height = finalHeight;
7487
+ if (!this.options.silentMode) {
7488
+ if (element === document.body || element === document.documentElement) {
7489
+ console.log(`📸 截取完整页面(document.body),使用压缩尺寸: ${finalWidth}x${finalHeight}`);
7246
7490
  }
7247
- }
7248
- else {
7249
- if (!this.options.silentMode) {
7250
- console.log(`📸 使用元素实际尺寸: ${elementWidth}x${elementHeight}`);
7491
+ else {
7492
+ console.log(`📸 使用压缩尺寸: ${finalWidth}x${finalHeight}`);
7251
7493
  }
7252
7494
  }
7253
7495
  }
7254
7496
  else {
7255
- // 对于 body,不设置尺寸限制,让 modern-screenshot 自动截取完整页面
7256
7497
  if (!this.options.silentMode) {
7257
- console.log(`📸 截取完整页面(document.body),不设置尺寸限制(让 modern-screenshot 自动处理)`);
7498
+ console.log(`📸 使用元素实际尺寸: ${elementWidth}x${elementHeight}`);
7258
7499
  }
7259
7500
  }
7260
- // 如果指定了缩放比例,添加缩放配置
7501
+ // 缩放配置:移动设备使用更低的缩放比例,进一步减少图片大小
7502
+ // scale < 1 会降低图片分辨率,减少 base64 大小
7261
7503
  if (this.options.scale !== 1) {
7262
- contextOptions.scale = isMobile ? 0.8 : this.options.scale;
7504
+ contextOptions.scale = isMobile ? 0.7 : this.options.scale; // 移动设备:0.8 -> 0.7
7505
+ }
7506
+ else if (isMobile) {
7507
+ // 如果未指定 scale,移动设备默认使用 0.7
7508
+ contextOptions.scale = 0.7;
7263
7509
  }
7264
- // modern-screenshot 会自动处理 worker URL,不需要手动设置
7510
+ // modern-screenshot 会自动处理 worker URL,不需要手动设置 workerUrl
7511
+ // 当 workerNumber > 0 时,截图处理会在 Worker 线程中执行,不会阻塞主线程 UI
7265
7512
  // 创建 Worker 上下文(每次截图都创建新的,确保元素状态最新)
7513
+ if (!this.options.silentMode) {
7514
+ console.log(`📸 Worker 模式: ${workerNumber} 个 Worker,质量: ${finalQuality.toFixed(2)},缩放: ${contextOptions.scale || 1}`);
7515
+ }
7266
7516
  this.screenshotContext = await createContext$1(element, contextOptions);
7267
7517
  try {
7268
7518
  // 使用 Worker 上下文进行截图
@@ -7291,7 +7541,8 @@ class ScreenshotManager {
7291
7541
  img.src = dataUrl;
7292
7542
  });
7293
7543
  let mimeType = 'image/jpeg';
7294
- let finalQuality = mobileQuality;
7544
+ // 使用与 createContext 相同的质量设置
7545
+ let conversionQuality = finalQuality;
7295
7546
  if (this.options.outputFormat === 'webp' && !isMobile) {
7296
7547
  try {
7297
7548
  const testCanvas = document.createElement('canvas');
@@ -7306,9 +7557,10 @@ class ScreenshotManager {
7306
7557
  mimeType = 'image/jpeg';
7307
7558
  }
7308
7559
  }
7560
+ // 使用优化后的质量进行格式转换
7309
7561
  const convertedDataUrl = mimeType === 'image/png'
7310
7562
  ? canvas.toDataURL(mimeType)
7311
- : canvas.toDataURL(mimeType, finalQuality);
7563
+ : canvas.toDataURL(mimeType, conversionQuality);
7312
7564
  return convertedDataUrl;
7313
7565
  }
7314
7566
  return dataUrl;
@@ -7784,6 +8036,87 @@ class ScreenshotManager {
7784
8036
  }
7785
8037
  return dataUrl;
7786
8038
  }
8039
+ /**
8040
+ * 不使用代理时下载图片(带内存保护)
8041
+ */
8042
+ async downloadImageWithoutProxy(url) {
8043
+ try {
8044
+ // 直接下载图片并检查大小
8045
+ const controller = new AbortController();
8046
+ const timeoutId = setTimeout(() => controller.abort(), this.options.imageLoadTimeout || 5000);
8047
+ const response = await fetch(url, {
8048
+ method: 'GET',
8049
+ mode: 'cors',
8050
+ credentials: 'omit',
8051
+ cache: 'no-cache',
8052
+ signal: controller.signal
8053
+ }).catch(() => null).finally(() => {
8054
+ clearTimeout(timeoutId);
8055
+ });
8056
+ if (response && response.ok) {
8057
+ // 检查 Content-Length(如果可用)
8058
+ const contentLength = response.headers.get('content-length');
8059
+ if (contentLength) {
8060
+ const sizeMB = parseInt(contentLength, 10) / (1024 * 1024);
8061
+ const maxSizeMB = this.options.maxImageSize || 5;
8062
+ if (sizeMB > maxSizeMB) {
8063
+ if (this.options.skipLargeImages) {
8064
+ // 跳过过大的图片,返回占位符
8065
+ if (!this.options.silentMode) {
8066
+ console.warn(`📸 ⚠️ 跳过过大图片(${sizeMB.toFixed(2)}MB > ${maxSizeMB}MB): ${url.substring(0, 100)}...`);
8067
+ }
8068
+ // 返回一个 1x1 的透明占位符,避免截图失败
8069
+ return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
8070
+ }
8071
+ else {
8072
+ // 不跳过,但添加警告
8073
+ if (!this.options.silentMode) {
8074
+ console.warn(`📸 ⚠️ 图片较大(${sizeMB.toFixed(2)}MB),可能导致内存问题: ${url.substring(0, 100)}...`);
8075
+ }
8076
+ }
8077
+ }
8078
+ }
8079
+ // 如果大小检查通过或无法获取大小,下载图片并转换为 data URL
8080
+ // 但为了进一步保护内存,在下载 blob 后也检查实际大小
8081
+ const blob = await response.blob();
8082
+ const blobSizeMB = blob.size / (1024 * 1024);
8083
+ const maxSizeMB = this.options.maxImageSize || 5;
8084
+ if (blobSizeMB > maxSizeMB) {
8085
+ if (this.options.skipLargeImages) {
8086
+ // 跳过过大的图片,返回占位符
8087
+ if (!this.options.silentMode) {
8088
+ console.warn(`📸 ⚠️ 跳过过大图片(实际大小 ${blobSizeMB.toFixed(2)}MB > ${maxSizeMB}MB): ${url.substring(0, 100)}...`);
8089
+ }
8090
+ return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
8091
+ }
8092
+ else {
8093
+ // 不跳过,但添加警告
8094
+ if (!this.options.silentMode) {
8095
+ console.warn(`📸 ⚠️ 图片较大(实际大小 ${blobSizeMB.toFixed(2)}MB),可能导致内存问题: ${url.substring(0, 100)}...`);
8096
+ }
8097
+ }
8098
+ }
8099
+ // 转换为 data URL
8100
+ const dataUrl = await this.blobToDataUrl(blob);
8101
+ // 缓存结果(带时间戳,10分钟有效)
8102
+ this.setCachedImage(url, dataUrl);
8103
+ // 如果启用 IndexedDB,也保存到 IndexedDB
8104
+ if (this.options.useIndexedDB) {
8105
+ await this.setIndexedDBCache(url, dataUrl);
8106
+ }
8107
+ return dataUrl;
8108
+ }
8109
+ // 如果下载失败,返回原 URL,让 modern-screenshot 自己处理
8110
+ return url;
8111
+ }
8112
+ catch (error) {
8113
+ // 下载失败,返回原 URL,让 modern-screenshot 自己处理
8114
+ if (!this.options.silentMode) {
8115
+ console.warn(`📸 ⚠️ 下载图片失败: ${url.substring(0, 100)}...`, error);
8116
+ }
8117
+ return url;
8118
+ }
8119
+ }
7787
8120
  /**
7788
8121
  * 将 blob 转换为 data URL
7789
8122
  */