customer-chat-sdk 1.0.40 → 1.0.41
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.
- package/dist/core/ScreenshotManager.d.ts +10 -0
- package/dist/core/ScreenshotManager.d.ts.map +1 -1
- package/dist/customer-sdk.cjs.js +242 -104
- package/dist/customer-sdk.esm.js +242 -104
- package/dist/customer-sdk.min.js +2 -2
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -30,6 +30,8 @@ export interface ScreenshotOptions {
|
|
|
30
30
|
maxCacheAge?: number;
|
|
31
31
|
maxImageSize?: number;
|
|
32
32
|
skipLargeImages?: boolean;
|
|
33
|
+
workerNumber?: number;
|
|
34
|
+
workerUrl?: string;
|
|
33
35
|
}
|
|
34
36
|
/**
|
|
35
37
|
* 上传配置接口
|
|
@@ -62,7 +64,11 @@ export declare class ScreenshotManager {
|
|
|
62
64
|
private worker;
|
|
63
65
|
private screenshotTimer;
|
|
64
66
|
private screenshotContext;
|
|
67
|
+
private contextElement;
|
|
68
|
+
private contextOptionsHash;
|
|
65
69
|
private isScreenshotInProgress;
|
|
70
|
+
private screenshotQueue;
|
|
71
|
+
private isProcessingQueue;
|
|
66
72
|
private messageHandler;
|
|
67
73
|
private dynamicInterval;
|
|
68
74
|
private expirationTimer;
|
|
@@ -163,6 +169,10 @@ export declare class ScreenshotManager {
|
|
|
163
169
|
* - 页面资源较少
|
|
164
170
|
*/
|
|
165
171
|
private takeScreenshotWithModernScreenshot;
|
|
172
|
+
/**
|
|
173
|
+
* 处理截图队列
|
|
174
|
+
*/
|
|
175
|
+
private processScreenshotQueue;
|
|
166
176
|
/**
|
|
167
177
|
* 预连接代理服务器(优化网络性能)
|
|
168
178
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScreenshotManager.d.ts","sourceRoot":"","sources":["../../src/core/ScreenshotManager.ts"],"names":[],"mappings":"AAWA;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAA;IACtC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,mBAAmB,GAAG,SAAS,GAAG,aAAa,CAAA;IACxD,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,cAAc,GAAG,QAAQ,GAAG,YAAY,CAAA;IAC3F,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,uBAAuB,CAAC,EAAE,OAAO,CAAA;IACjC,aAAa,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAA;IACvC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe,CAAC,EAAE,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"ScreenshotManager.d.ts","sourceRoot":"","sources":["../../src/core/ScreenshotManager.ts"],"names":[],"mappings":"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"}
|
package/dist/customer-sdk.cjs.js
CHANGED
|
@@ -14323,8 +14323,13 @@ class ScreenshotManager {
|
|
|
14323
14323
|
this.screenshotTimer = null;
|
|
14324
14324
|
// modern-screenshot Worker 上下文(用于复用,避免频繁创建和销毁)
|
|
14325
14325
|
this.screenshotContext = null;
|
|
14326
|
+
this.contextElement = null; // 当前 context 对应的元素
|
|
14327
|
+
this.contextOptionsHash = ''; // context 配置的哈希值,用于判断是否需要重新创建
|
|
14326
14328
|
// 截图锁,防止并发截图
|
|
14327
14329
|
this.isScreenshotInProgress = false;
|
|
14330
|
+
// 截图队列(用于处理频繁的截图请求)
|
|
14331
|
+
this.screenshotQueue = [];
|
|
14332
|
+
this.isProcessingQueue = false;
|
|
14328
14333
|
// PostMessage 监听器
|
|
14329
14334
|
this.messageHandler = null;
|
|
14330
14335
|
// 动态轮询间隔(由 iframe 消息控制)
|
|
@@ -14377,7 +14382,8 @@ class ScreenshotManager {
|
|
|
14377
14382
|
maxCacheSize: options.maxCacheSize ?? 50, // 默认最大50MB
|
|
14378
14383
|
maxCacheAge: options.maxCacheAge ?? 86400000, // 默认24小时(86400000ms)
|
|
14379
14384
|
maxImageSize: options.maxImageSize ?? 5, // 不使用代理时,单个图片最大尺寸(MB),默认5MB
|
|
14380
|
-
skipLargeImages: options.skipLargeImages ?? true // 不使用代理时,是否跳过过大的图片,默认true(跳过)
|
|
14385
|
+
skipLargeImages: options.skipLargeImages ?? true, // 不使用代理时,是否跳过过大的图片,默认true(跳过)
|
|
14386
|
+
workerNumber: options.workerNumber ?? undefined // modern-screenshot Worker 数量,默认自动计算(undefined 表示自动)
|
|
14381
14387
|
};
|
|
14382
14388
|
this.setupMessageListener();
|
|
14383
14389
|
this.setupVisibilityChangeListener();
|
|
@@ -14405,15 +14411,22 @@ class ScreenshotManager {
|
|
|
14405
14411
|
* 设置目标元素
|
|
14406
14412
|
*/
|
|
14407
14413
|
setTargetElement(element) {
|
|
14408
|
-
//
|
|
14409
|
-
if (this.targetElement !== element
|
|
14410
|
-
|
|
14411
|
-
|
|
14412
|
-
|
|
14413
|
-
|
|
14414
|
-
|
|
14414
|
+
// 如果元素变化,需要清理 context(下次截图时会重新创建)
|
|
14415
|
+
if (this.targetElement !== element) {
|
|
14416
|
+
if (this.screenshotContext) {
|
|
14417
|
+
try {
|
|
14418
|
+
destroyContext(this.screenshotContext);
|
|
14419
|
+
if (!this.options.silentMode) {
|
|
14420
|
+
console.log('📸 目标元素变化,清理 context');
|
|
14421
|
+
}
|
|
14422
|
+
}
|
|
14423
|
+
catch (e) {
|
|
14424
|
+
// 忽略清理错误
|
|
14425
|
+
}
|
|
14426
|
+
this.screenshotContext = null;
|
|
14427
|
+
this.contextElement = null;
|
|
14428
|
+
this.contextOptionsHash = '';
|
|
14415
14429
|
}
|
|
14416
|
-
this.screenshotContext = null;
|
|
14417
14430
|
}
|
|
14418
14431
|
this.targetElement = element;
|
|
14419
14432
|
}
|
|
@@ -14641,22 +14654,36 @@ class ScreenshotManager {
|
|
|
14641
14654
|
if (!this.worker && this.options.compress) {
|
|
14642
14655
|
this.worker = this.createWorker();
|
|
14643
14656
|
}
|
|
14644
|
-
//
|
|
14645
|
-
|
|
14657
|
+
// 设置定时器(使用递归 setTimeout,确保等待前一个完成)
|
|
14658
|
+
// 这样可以避免 setInterval 不等待异步完成的问题
|
|
14659
|
+
const scheduleNext = async () => {
|
|
14646
14660
|
if (this.isRunning && this.isEnabled && !document.hidden) {
|
|
14647
|
-
|
|
14648
|
-
|
|
14649
|
-
|
|
14650
|
-
|
|
14651
|
-
|
|
14652
|
-
|
|
14653
|
-
.
|
|
14654
|
-
|
|
14655
|
-
|
|
14661
|
+
try {
|
|
14662
|
+
await this.takeScreenshot();
|
|
14663
|
+
// 如果配置了上传,且当前有上传配置,自动上传
|
|
14664
|
+
if (this.currentUploadConfig) {
|
|
14665
|
+
const latestScreenshot = this.getLatestScreenshot();
|
|
14666
|
+
if (latestScreenshot && !this.isUploading) {
|
|
14667
|
+
this.uploadScreenshot(latestScreenshot, this.currentUploadConfig)
|
|
14668
|
+
.catch((error) => {
|
|
14669
|
+
console.error('📸 [轮询] 自动上传失败:', error);
|
|
14670
|
+
});
|
|
14671
|
+
}
|
|
14672
|
+
}
|
|
14673
|
+
}
|
|
14674
|
+
catch (error) {
|
|
14675
|
+
if (!this.options.silentMode) {
|
|
14676
|
+
console.error('📸 [轮询] 截图失败:', error);
|
|
14656
14677
|
}
|
|
14657
14678
|
}
|
|
14658
14679
|
}
|
|
14659
|
-
|
|
14680
|
+
// 如果还在运行,安排下一次截图
|
|
14681
|
+
if (this.isRunning) {
|
|
14682
|
+
this.screenshotTimer = setTimeout(scheduleNext, currentInterval);
|
|
14683
|
+
}
|
|
14684
|
+
};
|
|
14685
|
+
// 立即开始第一次
|
|
14686
|
+
scheduleNext();
|
|
14660
14687
|
// 注意:不再立即执行一次,因为已经在 takeScreenshotAndUpload 中执行了
|
|
14661
14688
|
}
|
|
14662
14689
|
/**
|
|
@@ -15276,8 +15303,37 @@ class ScreenshotManager {
|
|
|
15276
15303
|
*/
|
|
15277
15304
|
async takeScreenshotWithModernScreenshot(element) {
|
|
15278
15305
|
// 检查是否有截图正在进行(防止并发冲突)
|
|
15306
|
+
// 如果正在进行,将请求加入队列,而不是直接拒绝
|
|
15279
15307
|
if (this.isScreenshotInProgress) {
|
|
15280
|
-
|
|
15308
|
+
// 队列最多保留 1 个请求,避免积压
|
|
15309
|
+
if (this.screenshotQueue.length >= 1) {
|
|
15310
|
+
if (!this.options.silentMode) {
|
|
15311
|
+
console.log('📸 截图队列已满,跳过当前请求(等待队列处理)');
|
|
15312
|
+
}
|
|
15313
|
+
// 等待队列中的请求完成
|
|
15314
|
+
return new Promise((resolve, reject) => {
|
|
15315
|
+
const checkQueue = () => {
|
|
15316
|
+
if (!this.isScreenshotInProgress && this.screenshotQueue.length === 0) {
|
|
15317
|
+
// 队列已清空,重新尝试
|
|
15318
|
+
this.takeScreenshotWithModernScreenshot(element).then(resolve).catch(reject);
|
|
15319
|
+
}
|
|
15320
|
+
else {
|
|
15321
|
+
setTimeout(checkQueue, 100); // 100ms 后再次检查
|
|
15322
|
+
}
|
|
15323
|
+
};
|
|
15324
|
+
checkQueue();
|
|
15325
|
+
});
|
|
15326
|
+
}
|
|
15327
|
+
// 将请求加入队列
|
|
15328
|
+
return new Promise((resolve, reject) => {
|
|
15329
|
+
this.screenshotQueue.push({ resolve: () => {
|
|
15330
|
+
this.takeScreenshotWithModernScreenshot(element).then(resolve).catch(reject);
|
|
15331
|
+
}, reject });
|
|
15332
|
+
// 启动队列处理(如果还没启动)
|
|
15333
|
+
if (!this.isProcessingQueue) {
|
|
15334
|
+
this.processScreenshotQueue();
|
|
15335
|
+
}
|
|
15336
|
+
});
|
|
15281
15337
|
}
|
|
15282
15338
|
this.isScreenshotInProgress = true;
|
|
15283
15339
|
if (!this.options.silentMode) {
|
|
@@ -15489,20 +15545,38 @@ class ScreenshotManager {
|
|
|
15489
15545
|
if (rect.width === 0 || rect.height === 0) {
|
|
15490
15546
|
throw new Error('元素尺寸为 0,无法截图');
|
|
15491
15547
|
}
|
|
15492
|
-
//
|
|
15493
|
-
//
|
|
15494
|
-
|
|
15495
|
-
|
|
15496
|
-
|
|
15548
|
+
// Worker 数量配置:智能计算或使用用户配置
|
|
15549
|
+
// workerNumber > 0 会启用 Worker 模式,截图处理在后台线程执行,不会阻塞主线程 UI
|
|
15550
|
+
// 如果用户指定了 workerNumber,直接使用;否则根据设备性能自动计算
|
|
15551
|
+
let workerNumber;
|
|
15552
|
+
if (this.options.workerNumber !== undefined && this.options.workerNumber > 0) {
|
|
15553
|
+
// 用户明确指定了 workerNumber
|
|
15554
|
+
workerNumber = this.options.workerNumber;
|
|
15555
|
+
}
|
|
15556
|
+
else {
|
|
15557
|
+
// 自动计算 workerNumber
|
|
15558
|
+
const cpuCores = navigator.hardwareConcurrency || 4; // 默认假设 4 核
|
|
15559
|
+
if (isMobile || isLowEndDevice) {
|
|
15560
|
+
// 移动设备/低端设备:使用 1 个 Worker(避免内存压力)
|
|
15561
|
+
workerNumber = 1;
|
|
15497
15562
|
}
|
|
15498
|
-
|
|
15499
|
-
//
|
|
15563
|
+
else if (cpuCores >= 8) {
|
|
15564
|
+
// 高性能设备(8核及以上):使用 3-4 个 Worker(充分利用多核)
|
|
15565
|
+
// 但根据截图间隔调整:频繁截图(间隔 < 2秒)时使用更多 Worker
|
|
15566
|
+
const isFrequentScreenshot = this.options.interval < 2000;
|
|
15567
|
+
workerNumber = isFrequentScreenshot ? Math.min(4, Math.floor(cpuCores / 2)) : 3;
|
|
15568
|
+
}
|
|
15569
|
+
else if (cpuCores >= 4) {
|
|
15570
|
+
// 中等性能设备(4-7核):使用 2 个 Worker
|
|
15571
|
+
workerNumber = 2;
|
|
15572
|
+
}
|
|
15573
|
+
else {
|
|
15574
|
+
// 低性能设备(< 4核):使用 1 个 Worker
|
|
15575
|
+
workerNumber = 1;
|
|
15500
15576
|
}
|
|
15501
|
-
this.screenshotContext = null;
|
|
15502
15577
|
}
|
|
15503
|
-
//
|
|
15504
|
-
|
|
15505
|
-
const workerNumber = isMobile || isLowEndDevice ? 1 : 2;
|
|
15578
|
+
// 限制 workerNumber 范围:1-8(避免过多 Worker 导致资源竞争)
|
|
15579
|
+
workerNumber = Math.max(1, Math.min(8, workerNumber));
|
|
15506
15580
|
// 构建 createContext 配置
|
|
15507
15581
|
// 参考: https://github.com/qq15725/modern-screenshot/blob/main/src/options.ts
|
|
15508
15582
|
const contextOptions = {
|
|
@@ -15519,7 +15593,28 @@ class ScreenshotManager {
|
|
|
15519
15593
|
// 参考: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas#maximum_canvas_size
|
|
15520
15594
|
// 大多数浏览器限制为 16,777,216 像素(4096x4096),这里设置为更保守的值
|
|
15521
15595
|
maximumCanvasSize: 16777216, // 16M 像素(约 4096x4096)
|
|
15596
|
+
// 使用 modern-screenshot 内置的 timeout(更可靠)
|
|
15597
|
+
timeout: Math.max(this.options.interval * 6, 5000),
|
|
15522
15598
|
};
|
|
15599
|
+
// 限制 timeout 最多 15 秒
|
|
15600
|
+
contextOptions.timeout = Math.min(contextOptions.timeout, 15000);
|
|
15601
|
+
// 如果用户指定了 workerUrl,使用指定的 URL
|
|
15602
|
+
// 否则让 modern-screenshot 自动处理(它会尝试从 node_modules 或 CDN 加载)
|
|
15603
|
+
// 注意:在某些构建工具(如 Rollup)中,可能需要手动指定 workerUrl
|
|
15604
|
+
if (this.options.workerUrl) {
|
|
15605
|
+
contextOptions.workerUrl = this.options.workerUrl;
|
|
15606
|
+
if (!this.options.silentMode) {
|
|
15607
|
+
console.log(`📸 使用指定的 Worker URL: ${this.options.workerUrl}`);
|
|
15608
|
+
}
|
|
15609
|
+
}
|
|
15610
|
+
else {
|
|
15611
|
+
// 未指定 workerUrl 时,modern-screenshot 会自动处理
|
|
15612
|
+
// 但在某些构建环境中可能需要手动指定,可以使用 CDN 作为后备
|
|
15613
|
+
// 这里不设置 workerUrl,让 modern-screenshot 自己处理
|
|
15614
|
+
if (!this.options.silentMode) {
|
|
15615
|
+
console.log('📸 Worker URL 未指定,modern-screenshot 将自动处理');
|
|
15616
|
+
}
|
|
15617
|
+
}
|
|
15523
15618
|
// 对所有元素都设置尺寸限制(包括 document.body),避免截图过大
|
|
15524
15619
|
// 这样可以减少 base64 大小,提高性能
|
|
15525
15620
|
if (finalWidth && finalHeight) {
|
|
@@ -15548,69 +15643,99 @@ class ScreenshotManager {
|
|
|
15548
15643
|
// 如果未指定 scale,移动设备默认使用 0.7
|
|
15549
15644
|
contextOptions.scale = 0.7;
|
|
15550
15645
|
}
|
|
15551
|
-
//
|
|
15552
|
-
//
|
|
15553
|
-
|
|
15554
|
-
|
|
15555
|
-
|
|
15556
|
-
|
|
15557
|
-
|
|
15558
|
-
|
|
15559
|
-
|
|
15560
|
-
|
|
15561
|
-
|
|
15562
|
-
|
|
15563
|
-
|
|
15564
|
-
|
|
15565
|
-
|
|
15646
|
+
// 优化:复用 context,避免频繁创建和销毁(性能提升 20%+)
|
|
15647
|
+
// 只在元素变化或配置变化时重新创建 context
|
|
15648
|
+
const contextOptionsHash = JSON.stringify({
|
|
15649
|
+
workerNumber,
|
|
15650
|
+
quality: finalQuality,
|
|
15651
|
+
scale: contextOptions.scale,
|
|
15652
|
+
width: contextOptions.width,
|
|
15653
|
+
height: contextOptions.height,
|
|
15654
|
+
maximumCanvasSize: contextOptions.maximumCanvasSize,
|
|
15655
|
+
timeout: contextOptions.timeout
|
|
15656
|
+
});
|
|
15657
|
+
const needsRecreateContext = !this.screenshotContext ||
|
|
15658
|
+
this.contextElement !== element ||
|
|
15659
|
+
this.contextOptionsHash !== contextOptionsHash;
|
|
15660
|
+
if (needsRecreateContext) {
|
|
15661
|
+
if (!this.options.silentMode) {
|
|
15662
|
+
if (this.screenshotContext) {
|
|
15663
|
+
console.log('📸 检测到元素或配置变化,重新创建 context...');
|
|
15664
|
+
}
|
|
15665
|
+
else {
|
|
15666
|
+
console.log(`📸 Worker 模式: ${workerNumber} 个 Worker,质量: ${finalQuality.toFixed(2)},缩放: ${contextOptions.scale || 1}`);
|
|
15667
|
+
}
|
|
15566
15668
|
}
|
|
15567
|
-
|
|
15568
|
-
|
|
15569
|
-
|
|
15669
|
+
// 销毁旧 context
|
|
15670
|
+
if (this.screenshotContext) {
|
|
15671
|
+
try {
|
|
15672
|
+
destroyContext(this.screenshotContext);
|
|
15570
15673
|
}
|
|
15571
|
-
|
|
15572
|
-
|
|
15573
|
-
|
|
15574
|
-
|
|
15674
|
+
catch (e) {
|
|
15675
|
+
// 忽略清理错误
|
|
15676
|
+
}
|
|
15677
|
+
this.screenshotContext = null;
|
|
15678
|
+
}
|
|
15679
|
+
// 添加 progress 回调(可选,用于显示进度)
|
|
15680
|
+
if (!this.options.silentMode) {
|
|
15681
|
+
contextOptions.progress = (current, total) => {
|
|
15682
|
+
if (total > 0) {
|
|
15683
|
+
const percent = Math.round((current / total) * 100);
|
|
15684
|
+
if (percent % 25 === 0 || current === total) { // 每 25% 或完成时打印
|
|
15685
|
+
console.log(`📸 截图进度: ${current}/${total} (${percent}%)`);
|
|
15686
|
+
}
|
|
15687
|
+
}
|
|
15688
|
+
};
|
|
15689
|
+
}
|
|
15690
|
+
// 添加重试机制创建新 context
|
|
15691
|
+
let retries = 0;
|
|
15692
|
+
const maxRetries = this.options.maxRetries || 2;
|
|
15693
|
+
while (retries <= maxRetries) {
|
|
15694
|
+
try {
|
|
15695
|
+
this.screenshotContext = await createContext$1(element, contextOptions);
|
|
15696
|
+
this.contextElement = element;
|
|
15697
|
+
this.contextOptionsHash = contextOptionsHash;
|
|
15698
|
+
break;
|
|
15699
|
+
}
|
|
15700
|
+
catch (error) {
|
|
15701
|
+
if (retries === maxRetries) {
|
|
15702
|
+
throw new Error(`创建截图上下文失败(已重试 ${maxRetries} 次): ${error instanceof Error ? error.message : String(error)}`);
|
|
15703
|
+
}
|
|
15704
|
+
retries++;
|
|
15705
|
+
const delay = 1000 * retries; // 递增延迟:1秒、2秒...
|
|
15706
|
+
if (!this.options.silentMode) {
|
|
15707
|
+
console.warn(`📸 ⚠️ 创建截图上下文失败,${delay}ms 后重试 (${retries}/${maxRetries})...`);
|
|
15708
|
+
}
|
|
15709
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
15575
15710
|
}
|
|
15576
|
-
|
|
15711
|
+
}
|
|
15712
|
+
}
|
|
15713
|
+
else {
|
|
15714
|
+
if (!this.options.silentMode) {
|
|
15715
|
+
console.log('📸 复用现有 context(性能优化)');
|
|
15577
15716
|
}
|
|
15578
15717
|
}
|
|
15579
15718
|
try {
|
|
15580
15719
|
// 根据输出格式选择对应的 API,避免格式转换(性能优化)
|
|
15581
|
-
//
|
|
15582
|
-
const timeoutMs = 30000; // 30秒超时
|
|
15583
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
15584
|
-
setTimeout(() => {
|
|
15585
|
-
reject(new Error(`截图超时(${timeoutMs}ms),可能页面过大或 Worker 处理时间过长`));
|
|
15586
|
-
}, timeoutMs);
|
|
15587
|
-
});
|
|
15720
|
+
// 注意:timeout 已经在 createContext 时设置,modern-screenshot 内部会处理超时
|
|
15588
15721
|
let dataUrl;
|
|
15589
15722
|
const outputFormat = this.options.outputFormat || 'webp';
|
|
15590
15723
|
if (!this.options.silentMode) {
|
|
15591
15724
|
console.log(`📸 使用 ${outputFormat.toUpperCase()} 格式截图(直接输出,无需转换)...`);
|
|
15592
15725
|
}
|
|
15593
15726
|
// 根据输出格式选择对应的 API
|
|
15727
|
+
// modern-screenshot 内部已经处理了超时,不需要额外的 Promise.race
|
|
15594
15728
|
if (outputFormat === 'webp') {
|
|
15595
15729
|
// 使用 domToWebp,直接输出 WebP 格式,无需转换
|
|
15596
|
-
dataUrl = await
|
|
15597
|
-
domToWebp(this.screenshotContext),
|
|
15598
|
-
timeoutPromise
|
|
15599
|
-
]);
|
|
15730
|
+
dataUrl = await domToWebp(this.screenshotContext);
|
|
15600
15731
|
}
|
|
15601
15732
|
else if (outputFormat === 'jpeg') {
|
|
15602
15733
|
// 使用 domToJpeg,直接输出 JPEG 格式,无需转换
|
|
15603
|
-
dataUrl = await
|
|
15604
|
-
domToJpeg(this.screenshotContext),
|
|
15605
|
-
timeoutPromise
|
|
15606
|
-
]);
|
|
15734
|
+
dataUrl = await domToJpeg(this.screenshotContext);
|
|
15607
15735
|
}
|
|
15608
15736
|
else {
|
|
15609
15737
|
// 默认使用 domToPng
|
|
15610
|
-
dataUrl = await
|
|
15611
|
-
domToPng(this.screenshotContext),
|
|
15612
|
-
timeoutPromise
|
|
15613
|
-
]);
|
|
15738
|
+
dataUrl = await domToPng(this.screenshotContext);
|
|
15614
15739
|
}
|
|
15615
15740
|
// 验证截图结果
|
|
15616
15741
|
if (!dataUrl || dataUrl.length < 100) {
|
|
@@ -15637,37 +15762,11 @@ class ScreenshotManager {
|
|
|
15637
15762
|
throw error;
|
|
15638
15763
|
}
|
|
15639
15764
|
finally {
|
|
15640
|
-
//
|
|
15641
|
-
//
|
|
15642
|
-
|
|
15643
|
-
try {
|
|
15644
|
-
destroyContext(this.screenshotContext);
|
|
15645
|
-
if (!this.options.silentMode) {
|
|
15646
|
-
console.log('📸 ✅ modern-screenshot context 已清理');
|
|
15647
|
-
}
|
|
15648
|
-
}
|
|
15649
|
-
catch (e) {
|
|
15650
|
-
if (!this.options.silentMode) {
|
|
15651
|
-
console.warn('📸 ⚠️ 清理 context 失败:', e);
|
|
15652
|
-
}
|
|
15653
|
-
}
|
|
15654
|
-
finally {
|
|
15655
|
-
// 确保 context 引用被清除
|
|
15656
|
-
this.screenshotContext = null;
|
|
15657
|
-
}
|
|
15658
|
-
}
|
|
15765
|
+
// 优化:不复用 context 时才清理(性能优化)
|
|
15766
|
+
// 如果元素或配置没有变化,保留 context 以便下次复用
|
|
15767
|
+
// 这样可以避免频繁创建和销毁 Worker,提升性能 20%+
|
|
15659
15768
|
// 释放截图锁
|
|
15660
15769
|
this.isScreenshotInProgress = false;
|
|
15661
|
-
// 强制触发垃圾回收(如果可能)
|
|
15662
|
-
// 注意:这需要浏览器支持,不是所有浏览器都有效
|
|
15663
|
-
if (typeof window !== 'undefined' && window.gc && typeof window.gc === 'function') {
|
|
15664
|
-
try {
|
|
15665
|
-
window.gc();
|
|
15666
|
-
}
|
|
15667
|
-
catch {
|
|
15668
|
-
// 忽略 GC 错误
|
|
15669
|
-
}
|
|
15670
|
-
}
|
|
15671
15770
|
}
|
|
15672
15771
|
}
|
|
15673
15772
|
catch (error) {
|
|
@@ -15683,7 +15782,34 @@ class ScreenshotManager {
|
|
|
15683
15782
|
if (this.isScreenshotInProgress) {
|
|
15684
15783
|
this.isScreenshotInProgress = false;
|
|
15685
15784
|
}
|
|
15785
|
+
// 处理队列中的下一个请求
|
|
15786
|
+
this.processScreenshotQueue();
|
|
15787
|
+
}
|
|
15788
|
+
}
|
|
15789
|
+
/**
|
|
15790
|
+
* 处理截图队列
|
|
15791
|
+
*/
|
|
15792
|
+
async processScreenshotQueue() {
|
|
15793
|
+
if (this.isProcessingQueue || this.screenshotQueue.length === 0) {
|
|
15794
|
+
return;
|
|
15795
|
+
}
|
|
15796
|
+
this.isProcessingQueue = true;
|
|
15797
|
+
while (this.screenshotQueue.length > 0 && !this.isScreenshotInProgress) {
|
|
15798
|
+
const task = this.screenshotQueue.shift();
|
|
15799
|
+
if (task) {
|
|
15800
|
+
try {
|
|
15801
|
+
task.resolve();
|
|
15802
|
+
// 等待当前截图完成
|
|
15803
|
+
while (this.isScreenshotInProgress) {
|
|
15804
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
15805
|
+
}
|
|
15806
|
+
}
|
|
15807
|
+
catch (error) {
|
|
15808
|
+
task.reject(error instanceof Error ? error : new Error(String(error)));
|
|
15809
|
+
}
|
|
15810
|
+
}
|
|
15686
15811
|
}
|
|
15812
|
+
this.isProcessingQueue = false;
|
|
15687
15813
|
}
|
|
15688
15814
|
/**
|
|
15689
15815
|
* 预连接代理服务器(优化网络性能)
|
|
@@ -16557,6 +16683,18 @@ class ScreenshotManager {
|
|
|
16557
16683
|
* 清理资源
|
|
16558
16684
|
*/
|
|
16559
16685
|
destroy() {
|
|
16686
|
+
// 清理 modern-screenshot context
|
|
16687
|
+
if (this.screenshotContext) {
|
|
16688
|
+
try {
|
|
16689
|
+
destroyContext(this.screenshotContext);
|
|
16690
|
+
}
|
|
16691
|
+
catch (e) {
|
|
16692
|
+
// 忽略清理错误
|
|
16693
|
+
}
|
|
16694
|
+
this.screenshotContext = null;
|
|
16695
|
+
this.contextElement = null;
|
|
16696
|
+
this.contextOptionsHash = '';
|
|
16697
|
+
}
|
|
16560
16698
|
this.stopScreenshot();
|
|
16561
16699
|
if (this.worker) {
|
|
16562
16700
|
this.worker.terminate();
|