customer-chat-sdk 1.0.45 → 1.0.47
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.
|
@@ -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":"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;IACzB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;CACjB;AA4BD;;;GAGG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,OAAO,CAAyH;IACxI,OAAO,CAAC,aAAa,CAA2B;IAChD,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,eAAe,CAAI;IAC3B,OAAO,CAAC,iBAAiB,CAAe;IACxC,OAAO,CAAC,kBAAkB,CAAI;IAC9B,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,SAAS,CAAQ;IAGzB,OAAO,CAAC,WAAW,CAAsB;IACzC,OAAO,CAAC,mBAAmB,CAA4B;IACvD,OAAO,CAAC,oBAAoB,CAAqC;IAGjE,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,eAAe,CAA8B;IAGrD,OAAO,CAAC,iBAAiB,CAAY;IACrC,OAAO,CAAC,cAAc,CAA2B;IACjD,OAAO,CAAC,kBAAkB,CAAa;IACvC,OAAO,CAAC,kBAAkB,CAAa;IACvC,OAAO,CAAC,qBAAqB,CAAY;IACzC,OAAO,CAAC,aAAa,CAAe;IAGpC,OAAO,CAAC,sBAAsB,CAAQ;IAGtC,OAAO,CAAC,eAAe,CAAqE;IAC5F,OAAO,CAAC,iBAAiB,CAAQ;IAGjC,OAAO,CAAC,cAAc,CAA8D;IAGpF,OAAO,CAAC,eAAe,CAAsB;IAG7C,OAAO,CAAC,eAAe,CAA8B;IAGrD,OAAO,CAAC,eAAe,CAA4D;IAGnF,OAAO,CAAC,cAAc,CAA2B;IACjD,OAAO,CAAC,cAAc,CAAQ;IAG9B,OAAO,CAAC,oBAAoB,CAAoC;IAChE,OAAO,CAAC,oBAAoB,CAAyB;IAGrD,OAAO,CAAC,YAAY,CAAQ;IAG5B,OAAO,CAAC,kBAAkB,CAAqC;IAC/D,OAAO,CAAC,eAAe,CAAoB;IAC3C,OAAO,CAAC,2BAA2B,CAAI;IAGvC,OAAO,CAAC,kBAAkB,CAA6C;IACvE,OAAO,CAAC,sBAAsB,CAAwD;gBAE1E,aAAa,EAAE,WAAW,GAAG,IAAI,EAAE,OAAO,GAAE,iBAAsB,EAAE,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI;IAqElH;;OAEG;IACH,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI;IAsBnD;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAgE5B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAgB5B;;OAEG;IACH,OAAO,CAAC,6BAA6B;IAcrC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA6F3B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA+BzB;;OAEG;IACH,eAAe,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI;IAmG9C;;OAEG;IACH,cAAc,IAAI,IAAI;IAiBtB;;OAEG;IACG,WAAW,CAAC,KAAK,GAAE,OAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IAS3D;;OAEG;YACW,cAAc;IA2K5B;;;;;;OAMG;YACW,yBAAyB;IAoGvC;;;;;;;;;;;;;;;;OAgBG;YACW,6BAA6B;IAkW3C;;;;;;;;;;;;;;;;;OAiBG;YACW,kCAAkC;IAopBhD;;OAEG;YACW,sBAAsB;IA0BpC;;OAEG;IACH,OAAO,CAAC,eAAe;IAsBvB;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAuBhC;;OAEG;YACW,aAAa;IA4B3B;;OAEG;YACW,iBAAiB;IAsC/B;;OAEG;YACW,iBAAiB;IAwB/B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAO3B;;OAEG;YACW,qBAAqB;IA0BnC;;OAEG;YACW,mBAAmB;IAiEjC;;OAEG;YACW,yBAAyB;IAuDvC;;OAEG;YACW,oBAAoB;IAclC;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA+CxB;;OAEG;YACW,uBAAuB;IA2ErC;;OAEG;YACW,UAAU;IAmDxB;;OAEG;YACW,yBAAyB;IAsFvC;;OAEG;IACH,OAAO,CAAC,aAAa;IAqBrB;;OAEG;YACW,mBAAmB;IA8BjC;;;OAGG;YACW,qBAAqB;IAqCnC;;OAEG;YACW,YAAY;IAa1B;;;OAGG;YACW,sBAAsB;IAoEpC;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAe/B;;OAEG;IACH,OAAO,CAAC,YAAY;IAuQpB;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAkChC;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAYjC;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAa5B;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IA8BzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAazB;;OAEG;YACW,2BAA2B;IA8EzC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAI3B;;OAEG;IACH,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAO9B;;OAEG;IACH,OAAO,IAAI,IAAI;IAoEf;;OAEG;IACH,OAAO,CAAC,cAAc;IAkBtB;;;OAGG;IACH,OAAO,CAAC,cAAc;IAqCtB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAoBzB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAsBzB;;OAEG;IACH,QAAQ;;;;;;;;;CAWT"}
|
package/dist/customer-sdk.cjs.js
CHANGED
|
@@ -14359,7 +14359,7 @@ class ScreenshotManager {
|
|
|
14359
14359
|
this.sendToIframeCallback = sendToIframe || null;
|
|
14360
14360
|
this.options = {
|
|
14361
14361
|
interval: options.interval ?? 1000,
|
|
14362
|
-
quality: options.quality ?? 0.
|
|
14362
|
+
quality: options.quality ?? 0.15, // 降低默认质量:0.3 -> 0.15,进一步减少 base64 大小
|
|
14363
14363
|
scale: options.scale ?? 1,
|
|
14364
14364
|
maxHistory: options.maxHistory ?? 10,
|
|
14365
14365
|
compress: options.compress ?? false,
|
|
@@ -15078,16 +15078,18 @@ class ScreenshotManager {
|
|
|
15078
15078
|
elementHeight = Math.max(element.scrollHeight, element.offsetHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight, window.innerHeight);
|
|
15079
15079
|
}
|
|
15080
15080
|
// html2canvas 质量设置(0-1)
|
|
15081
|
+
// 进一步降低质量以减少文件大小
|
|
15081
15082
|
const finalQuality = isMobile || isLowEndDevice
|
|
15082
|
-
? Math.max(this.options.quality * 0.65, 0.
|
|
15083
|
-
: this.options.quality;
|
|
15083
|
+
? Math.max(this.options.quality * 0.65, 0.15) // 移动设备:最低 0.15
|
|
15084
|
+
: Math.max(this.options.quality, 0.15); // 桌面设备:最低 0.15
|
|
15084
15085
|
// html2canvas 配置选项
|
|
15085
15086
|
// 注意:不要设置 width 和 height,让 html2canvas 自动计算元素的实际尺寸
|
|
15086
15087
|
// 设置固定的 width/height 会导致截图尺寸不正确
|
|
15087
15088
|
const options = {
|
|
15088
15089
|
// 基本配置
|
|
15089
15090
|
backgroundColor: '#ffffff',
|
|
15090
|
-
|
|
15091
|
+
// 降低缩放比例以减少文件大小(默认 0.6,移动设备 0.5)
|
|
15092
|
+
scale: this.options.scale !== 1 ? (isMobile ? 0.5 : this.options.scale) : (isMobile ? 0.5 : 0.6),
|
|
15091
15093
|
useCORS: this.options.enableCORS,
|
|
15092
15094
|
allowTaint: !this.options.enableCORS, // 如果启用 CORS,不允许 taint
|
|
15093
15095
|
logging: !this.options.silentMode,
|
|
@@ -16695,25 +16697,219 @@ class ScreenshotManager {
|
|
|
16695
16697
|
return { width, height };
|
|
16696
16698
|
}
|
|
16697
16699
|
/**
|
|
16698
|
-
* 创建 WebWorker
|
|
16700
|
+
* 创建 WebWorker(实现类似 TinyPNG 的智能压缩)
|
|
16699
16701
|
*/
|
|
16700
16702
|
createWorker() {
|
|
16701
|
-
if (typeof Worker === 'undefined'
|
|
16703
|
+
if (typeof Worker === 'undefined') {
|
|
16702
16704
|
return null;
|
|
16703
16705
|
}
|
|
16704
16706
|
try {
|
|
16705
|
-
//
|
|
16707
|
+
// 完整的 Worker 压缩代码(类似 TinyPNG 的智能压缩)
|
|
16706
16708
|
const workerCode = `
|
|
16707
|
-
|
|
16709
|
+
// 图片压缩 Worker(类似 TinyPNG 的智能压缩)
|
|
16710
|
+
self.onmessage = async function(e) {
|
|
16708
16711
|
const { type, data } = e.data;
|
|
16712
|
+
|
|
16709
16713
|
if (type === 'COMPRESS_IMAGE') {
|
|
16710
|
-
|
|
16711
|
-
|
|
16712
|
-
|
|
16713
|
-
|
|
16714
|
-
|
|
16714
|
+
try {
|
|
16715
|
+
const { dataUrl, maxWidth, maxHeight, quality, outputFormat } = data;
|
|
16716
|
+
|
|
16717
|
+
// 1. 加载图片
|
|
16718
|
+
const img = await loadImage(dataUrl);
|
|
16719
|
+
|
|
16720
|
+
// 获取图片尺寸(ImageBitmap 或 Image 都支持)
|
|
16721
|
+
const imgWidth = img.width || img.naturalWidth || 0;
|
|
16722
|
+
const imgHeight = img.height || img.naturalHeight || 0;
|
|
16723
|
+
|
|
16724
|
+
if (imgWidth === 0 || imgHeight === 0) {
|
|
16725
|
+
throw new Error('无法获取图片尺寸');
|
|
16726
|
+
}
|
|
16727
|
+
|
|
16728
|
+
// 2. 计算压缩后的尺寸(保持宽高比)
|
|
16729
|
+
const { width, height } = calculateSize(
|
|
16730
|
+
imgWidth,
|
|
16731
|
+
imgHeight,
|
|
16732
|
+
maxWidth || 1600,
|
|
16733
|
+
maxHeight || 900
|
|
16734
|
+
);
|
|
16735
|
+
|
|
16736
|
+
// 3. 创建 OffscreenCanvas(Worker 中必须使用 OffscreenCanvas)
|
|
16737
|
+
if (typeof OffscreenCanvas === 'undefined') {
|
|
16738
|
+
throw new Error('浏览器不支持 OffscreenCanvas,无法在 Worker 中压缩');
|
|
16739
|
+
}
|
|
16740
|
+
|
|
16741
|
+
const canvas = new OffscreenCanvas(width, height);
|
|
16742
|
+
const ctx = canvas.getContext('2d');
|
|
16743
|
+
|
|
16744
|
+
if (!ctx) {
|
|
16745
|
+
throw new Error('无法获取 Canvas 上下文');
|
|
16746
|
+
}
|
|
16747
|
+
|
|
16748
|
+
// 4. 优化绘制设置(提升压缩质量)
|
|
16749
|
+
ctx.imageSmoothingEnabled = true;
|
|
16750
|
+
ctx.imageSmoothingQuality = 'high';
|
|
16751
|
+
|
|
16752
|
+
// 5. 使用白色背景(避免透明区域压缩问题)
|
|
16753
|
+
ctx.fillStyle = '#ffffff';
|
|
16754
|
+
ctx.fillRect(0, 0, width, height);
|
|
16755
|
+
|
|
16756
|
+
// 6. 绘制图片(高质量缩放)
|
|
16757
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
16758
|
+
|
|
16759
|
+
// 7. 转换为目标格式(智能质量调整)
|
|
16760
|
+
// OffscreenCanvas 使用 convertToBlob 而不是 toDataURL
|
|
16761
|
+
let compressedDataUrl;
|
|
16762
|
+
const finalQuality = Math.max(0.1, Math.min(0.95, quality || 0.15));
|
|
16763
|
+
|
|
16764
|
+
// 辅助函数:将 Blob 转换为 data URL
|
|
16765
|
+
async function blobToDataURL(blob) {
|
|
16766
|
+
return new Promise((resolve, reject) => {
|
|
16767
|
+
const reader = new FileReader();
|
|
16768
|
+
reader.onload = () => resolve(reader.result);
|
|
16769
|
+
reader.onerror = reject;
|
|
16770
|
+
reader.readAsDataURL(blob);
|
|
16771
|
+
});
|
|
16772
|
+
}
|
|
16773
|
+
|
|
16774
|
+
// 确定 MIME 类型
|
|
16775
|
+
let mimeType = 'image/png';
|
|
16776
|
+
if (outputFormat === 'webp') {
|
|
16777
|
+
mimeType = 'image/webp';
|
|
16778
|
+
} else if (outputFormat === 'jpeg') {
|
|
16779
|
+
mimeType = 'image/jpeg';
|
|
16780
|
+
}
|
|
16781
|
+
|
|
16782
|
+
// 第一次压缩
|
|
16783
|
+
let blob = await canvas.convertToBlob({
|
|
16784
|
+
type: mimeType,
|
|
16785
|
+
quality: finalQuality
|
|
16786
|
+
});
|
|
16787
|
+
|
|
16788
|
+
compressedDataUrl = await blobToDataURL(blob);
|
|
16789
|
+
|
|
16790
|
+
// 8. 如果压缩后文件仍然较大,尝试进一步降低质量
|
|
16791
|
+
const originalSize = dataUrl.length;
|
|
16792
|
+
let compressedSize = compressedDataUrl.length;
|
|
16793
|
+
|
|
16794
|
+
// 如果压缩后大小减少不足 20%,尝试更激进的压缩
|
|
16795
|
+
if (compressedSize > originalSize * 0.8 && finalQuality > 0.1 && (outputFormat === 'webp' || outputFormat === 'jpeg')) {
|
|
16796
|
+
const aggressiveQuality = Math.max(0.1, finalQuality * 0.7);
|
|
16797
|
+
|
|
16798
|
+
try {
|
|
16799
|
+
const moreCompressedBlob = await canvas.convertToBlob({
|
|
16800
|
+
type: mimeType,
|
|
16801
|
+
quality: aggressiveQuality
|
|
16802
|
+
});
|
|
16803
|
+
|
|
16804
|
+
const moreCompressedDataUrl = await blobToDataURL(moreCompressedBlob);
|
|
16805
|
+
|
|
16806
|
+
// 如果更激进的压缩效果更好,使用它
|
|
16807
|
+
if (moreCompressedDataUrl.length < compressedDataUrl.length) {
|
|
16808
|
+
compressedDataUrl = moreCompressedDataUrl;
|
|
16809
|
+
compressedSize = compressedDataUrl.length;
|
|
16810
|
+
}
|
|
16811
|
+
} catch (e) {
|
|
16812
|
+
// 忽略错误,使用之前的压缩结果
|
|
16813
|
+
}
|
|
16814
|
+
}
|
|
16815
|
+
|
|
16816
|
+
// 9. 返回压缩结果
|
|
16817
|
+
self.postMessage({
|
|
16818
|
+
type: 'SCREENSHOT_RESULT',
|
|
16819
|
+
data: {
|
|
16820
|
+
compressed: {
|
|
16821
|
+
dataUrl: compressedDataUrl,
|
|
16822
|
+
originalSize: originalSize,
|
|
16823
|
+
compressedSize: compressedDataUrl.length,
|
|
16824
|
+
compressionRatio: ((1 - compressedDataUrl.length / originalSize) * 100).toFixed(1)
|
|
16825
|
+
}
|
|
16826
|
+
}
|
|
16827
|
+
});
|
|
16828
|
+
} catch (error) {
|
|
16829
|
+
// 压缩失败,返回原始数据
|
|
16830
|
+
self.postMessage({
|
|
16831
|
+
type: 'SCREENSHOT_RESULT',
|
|
16832
|
+
data: {
|
|
16833
|
+
compressed: {
|
|
16834
|
+
dataUrl: data.dataUrl,
|
|
16835
|
+
error: error.message
|
|
16836
|
+
}
|
|
16837
|
+
}
|
|
16838
|
+
});
|
|
16839
|
+
}
|
|
16715
16840
|
}
|
|
16716
16841
|
};
|
|
16842
|
+
|
|
16843
|
+
// 加载图片的辅助函数(Worker 中使用 createImageBitmap)
|
|
16844
|
+
async function loadImage(dataUrl) {
|
|
16845
|
+
try {
|
|
16846
|
+
// 将 data URL 转换为 Blob
|
|
16847
|
+
let blob;
|
|
16848
|
+
if (typeof fetch !== 'undefined') {
|
|
16849
|
+
try {
|
|
16850
|
+
const response = await fetch(dataUrl);
|
|
16851
|
+
blob = await response.blob();
|
|
16852
|
+
} catch (e) {
|
|
16853
|
+
// fetch 失败,手动转换 data URL 到 Blob
|
|
16854
|
+
blob = dataURLToBlob(dataUrl);
|
|
16855
|
+
}
|
|
16856
|
+
} else {
|
|
16857
|
+
// 没有 fetch,手动转换
|
|
16858
|
+
blob = dataURLToBlob(dataUrl);
|
|
16859
|
+
}
|
|
16860
|
+
|
|
16861
|
+
// 使用 createImageBitmap 创建图片(Worker 中推荐方式)
|
|
16862
|
+
if (typeof createImageBitmap !== 'undefined') {
|
|
16863
|
+
return await createImageBitmap(blob);
|
|
16864
|
+
} else {
|
|
16865
|
+
// 回退方案:使用 Image(某些 Worker 环境可能支持)
|
|
16866
|
+
return new Promise((resolve, reject) => {
|
|
16867
|
+
const img = new Image();
|
|
16868
|
+
img.onload = () => resolve(img);
|
|
16869
|
+
img.onerror = reject;
|
|
16870
|
+
img.src = dataUrl;
|
|
16871
|
+
});
|
|
16872
|
+
}
|
|
16873
|
+
} catch (error) {
|
|
16874
|
+
// 如果 createImageBitmap 失败,尝试直接使用 Image
|
|
16875
|
+
return new Promise((resolve, reject) => {
|
|
16876
|
+
const img = new Image();
|
|
16877
|
+
img.onload = () => resolve(img);
|
|
16878
|
+
img.onerror = () => reject(error);
|
|
16879
|
+
img.src = dataUrl;
|
|
16880
|
+
});
|
|
16881
|
+
}
|
|
16882
|
+
}
|
|
16883
|
+
|
|
16884
|
+
// 将 data URL 转换为 Blob 的辅助函数
|
|
16885
|
+
function dataURLToBlob(dataUrl) {
|
|
16886
|
+
const arr = dataUrl.split(',');
|
|
16887
|
+
const mimeMatch = arr[0].match(/:(.*?);/);
|
|
16888
|
+
const mime = mimeMatch ? mimeMatch[1] : 'image/png';
|
|
16889
|
+
const bstr = atob(arr[1]);
|
|
16890
|
+
let n = bstr.length;
|
|
16891
|
+
const u8arr = new Uint8Array(n);
|
|
16892
|
+
while (n--) {
|
|
16893
|
+
u8arr[n] = bstr.charCodeAt(n);
|
|
16894
|
+
}
|
|
16895
|
+
return new Blob([u8arr], { type: mime });
|
|
16896
|
+
}
|
|
16897
|
+
|
|
16898
|
+
// 计算压缩后尺寸的辅助函数(保持宽高比)
|
|
16899
|
+
function calculateSize(originalWidth, originalHeight, maxWidth, maxHeight) {
|
|
16900
|
+
let width = originalWidth;
|
|
16901
|
+
let height = originalHeight;
|
|
16902
|
+
|
|
16903
|
+
if (width > maxWidth || height > maxHeight) {
|
|
16904
|
+
const widthRatio = maxWidth / width;
|
|
16905
|
+
const heightRatio = maxHeight / height;
|
|
16906
|
+
const ratio = Math.min(widthRatio, heightRatio);
|
|
16907
|
+
width = Math.round(width * ratio);
|
|
16908
|
+
height = Math.round(height * ratio);
|
|
16909
|
+
}
|
|
16910
|
+
|
|
16911
|
+
return { width, height };
|
|
16912
|
+
}
|
|
16717
16913
|
`;
|
|
16718
16914
|
const blob = new Blob([workerCode], { type: 'application/javascript' });
|
|
16719
16915
|
const workerUrl = URL.createObjectURL(blob);
|
|
@@ -16721,17 +16917,34 @@ class ScreenshotManager {
|
|
|
16721
16917
|
newWorker.onmessage = (e) => {
|
|
16722
16918
|
const { type, data } = e.data;
|
|
16723
16919
|
if (type === 'SCREENSHOT_RESULT' && data?.compressed) {
|
|
16920
|
+
const compressed = data.compressed;
|
|
16921
|
+
// 更新截图历史记录
|
|
16724
16922
|
if (this.screenshotHistory.length > 0) {
|
|
16725
|
-
this.screenshotHistory[this.screenshotHistory.length - 1] =
|
|
16923
|
+
this.screenshotHistory[this.screenshotHistory.length - 1] = compressed.dataUrl;
|
|
16924
|
+
// 打印压缩统计信息
|
|
16925
|
+
if (!this.options.silentMode && compressed.originalSize && compressed.compressedSize) {
|
|
16926
|
+
const originalKB = (compressed.originalSize * 0.75 / 1024).toFixed(2);
|
|
16927
|
+
const compressedKB = (compressed.compressedSize * 0.75 / 1024).toFixed(2);
|
|
16928
|
+
const ratio = compressed.compressionRatio || '0';
|
|
16929
|
+
console.log('📸 [Worker 压缩] ✅ 压缩完成');
|
|
16930
|
+
console.log(` 原始大小: ${originalKB} KB`);
|
|
16931
|
+
console.log(` 压缩后: ${compressedKB} KB`);
|
|
16932
|
+
console.log(` 压缩率: ${ratio}%`);
|
|
16933
|
+
if (compressed.error) {
|
|
16934
|
+
console.warn(` ⚠️ 压缩警告: ${compressed.error}`);
|
|
16935
|
+
}
|
|
16936
|
+
}
|
|
16726
16937
|
}
|
|
16727
16938
|
}
|
|
16728
16939
|
};
|
|
16729
16940
|
newWorker.onerror = (e) => {
|
|
16730
16941
|
console.error('📸 WebWorker 错误:', e);
|
|
16942
|
+
if (!this.options.silentMode) {
|
|
16943
|
+
console.warn('📸 Worker 压缩失败,使用原始截图');
|
|
16944
|
+
}
|
|
16731
16945
|
};
|
|
16732
16946
|
// 注意:不要立即 revokeObjectURL,因为 Worker 需要这个 URL 保持有效
|
|
16733
16947
|
// 在 destroy() 方法中清理 Worker 时再 revoke
|
|
16734
|
-
// URL.revokeObjectURL(workerUrl) // 已移除,在 destroy 时清理
|
|
16735
16948
|
return newWorker;
|
|
16736
16949
|
}
|
|
16737
16950
|
catch (err) {
|
package/dist/customer-sdk.esm.js
CHANGED
|
@@ -14355,7 +14355,7 @@ class ScreenshotManager {
|
|
|
14355
14355
|
this.sendToIframeCallback = sendToIframe || null;
|
|
14356
14356
|
this.options = {
|
|
14357
14357
|
interval: options.interval ?? 1000,
|
|
14358
|
-
quality: options.quality ?? 0.
|
|
14358
|
+
quality: options.quality ?? 0.15, // 降低默认质量:0.3 -> 0.15,进一步减少 base64 大小
|
|
14359
14359
|
scale: options.scale ?? 1,
|
|
14360
14360
|
maxHistory: options.maxHistory ?? 10,
|
|
14361
14361
|
compress: options.compress ?? false,
|
|
@@ -15074,16 +15074,18 @@ class ScreenshotManager {
|
|
|
15074
15074
|
elementHeight = Math.max(element.scrollHeight, element.offsetHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight, window.innerHeight);
|
|
15075
15075
|
}
|
|
15076
15076
|
// html2canvas 质量设置(0-1)
|
|
15077
|
+
// 进一步降低质量以减少文件大小
|
|
15077
15078
|
const finalQuality = isMobile || isLowEndDevice
|
|
15078
|
-
? Math.max(this.options.quality * 0.65, 0.
|
|
15079
|
-
: this.options.quality;
|
|
15079
|
+
? Math.max(this.options.quality * 0.65, 0.15) // 移动设备:最低 0.15
|
|
15080
|
+
: Math.max(this.options.quality, 0.15); // 桌面设备:最低 0.15
|
|
15080
15081
|
// html2canvas 配置选项
|
|
15081
15082
|
// 注意:不要设置 width 和 height,让 html2canvas 自动计算元素的实际尺寸
|
|
15082
15083
|
// 设置固定的 width/height 会导致截图尺寸不正确
|
|
15083
15084
|
const options = {
|
|
15084
15085
|
// 基本配置
|
|
15085
15086
|
backgroundColor: '#ffffff',
|
|
15086
|
-
|
|
15087
|
+
// 降低缩放比例以减少文件大小(默认 0.6,移动设备 0.5)
|
|
15088
|
+
scale: this.options.scale !== 1 ? (isMobile ? 0.5 : this.options.scale) : (isMobile ? 0.5 : 0.6),
|
|
15087
15089
|
useCORS: this.options.enableCORS,
|
|
15088
15090
|
allowTaint: !this.options.enableCORS, // 如果启用 CORS,不允许 taint
|
|
15089
15091
|
logging: !this.options.silentMode,
|
|
@@ -16691,25 +16693,219 @@ class ScreenshotManager {
|
|
|
16691
16693
|
return { width, height };
|
|
16692
16694
|
}
|
|
16693
16695
|
/**
|
|
16694
|
-
* 创建 WebWorker
|
|
16696
|
+
* 创建 WebWorker(实现类似 TinyPNG 的智能压缩)
|
|
16695
16697
|
*/
|
|
16696
16698
|
createWorker() {
|
|
16697
|
-
if (typeof Worker === 'undefined'
|
|
16699
|
+
if (typeof Worker === 'undefined') {
|
|
16698
16700
|
return null;
|
|
16699
16701
|
}
|
|
16700
16702
|
try {
|
|
16701
|
-
//
|
|
16703
|
+
// 完整的 Worker 压缩代码(类似 TinyPNG 的智能压缩)
|
|
16702
16704
|
const workerCode = `
|
|
16703
|
-
|
|
16705
|
+
// 图片压缩 Worker(类似 TinyPNG 的智能压缩)
|
|
16706
|
+
self.onmessage = async function(e) {
|
|
16704
16707
|
const { type, data } = e.data;
|
|
16708
|
+
|
|
16705
16709
|
if (type === 'COMPRESS_IMAGE') {
|
|
16706
|
-
|
|
16707
|
-
|
|
16708
|
-
|
|
16709
|
-
|
|
16710
|
-
|
|
16710
|
+
try {
|
|
16711
|
+
const { dataUrl, maxWidth, maxHeight, quality, outputFormat } = data;
|
|
16712
|
+
|
|
16713
|
+
// 1. 加载图片
|
|
16714
|
+
const img = await loadImage(dataUrl);
|
|
16715
|
+
|
|
16716
|
+
// 获取图片尺寸(ImageBitmap 或 Image 都支持)
|
|
16717
|
+
const imgWidth = img.width || img.naturalWidth || 0;
|
|
16718
|
+
const imgHeight = img.height || img.naturalHeight || 0;
|
|
16719
|
+
|
|
16720
|
+
if (imgWidth === 0 || imgHeight === 0) {
|
|
16721
|
+
throw new Error('无法获取图片尺寸');
|
|
16722
|
+
}
|
|
16723
|
+
|
|
16724
|
+
// 2. 计算压缩后的尺寸(保持宽高比)
|
|
16725
|
+
const { width, height } = calculateSize(
|
|
16726
|
+
imgWidth,
|
|
16727
|
+
imgHeight,
|
|
16728
|
+
maxWidth || 1600,
|
|
16729
|
+
maxHeight || 900
|
|
16730
|
+
);
|
|
16731
|
+
|
|
16732
|
+
// 3. 创建 OffscreenCanvas(Worker 中必须使用 OffscreenCanvas)
|
|
16733
|
+
if (typeof OffscreenCanvas === 'undefined') {
|
|
16734
|
+
throw new Error('浏览器不支持 OffscreenCanvas,无法在 Worker 中压缩');
|
|
16735
|
+
}
|
|
16736
|
+
|
|
16737
|
+
const canvas = new OffscreenCanvas(width, height);
|
|
16738
|
+
const ctx = canvas.getContext('2d');
|
|
16739
|
+
|
|
16740
|
+
if (!ctx) {
|
|
16741
|
+
throw new Error('无法获取 Canvas 上下文');
|
|
16742
|
+
}
|
|
16743
|
+
|
|
16744
|
+
// 4. 优化绘制设置(提升压缩质量)
|
|
16745
|
+
ctx.imageSmoothingEnabled = true;
|
|
16746
|
+
ctx.imageSmoothingQuality = 'high';
|
|
16747
|
+
|
|
16748
|
+
// 5. 使用白色背景(避免透明区域压缩问题)
|
|
16749
|
+
ctx.fillStyle = '#ffffff';
|
|
16750
|
+
ctx.fillRect(0, 0, width, height);
|
|
16751
|
+
|
|
16752
|
+
// 6. 绘制图片(高质量缩放)
|
|
16753
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
16754
|
+
|
|
16755
|
+
// 7. 转换为目标格式(智能质量调整)
|
|
16756
|
+
// OffscreenCanvas 使用 convertToBlob 而不是 toDataURL
|
|
16757
|
+
let compressedDataUrl;
|
|
16758
|
+
const finalQuality = Math.max(0.1, Math.min(0.95, quality || 0.15));
|
|
16759
|
+
|
|
16760
|
+
// 辅助函数:将 Blob 转换为 data URL
|
|
16761
|
+
async function blobToDataURL(blob) {
|
|
16762
|
+
return new Promise((resolve, reject) => {
|
|
16763
|
+
const reader = new FileReader();
|
|
16764
|
+
reader.onload = () => resolve(reader.result);
|
|
16765
|
+
reader.onerror = reject;
|
|
16766
|
+
reader.readAsDataURL(blob);
|
|
16767
|
+
});
|
|
16768
|
+
}
|
|
16769
|
+
|
|
16770
|
+
// 确定 MIME 类型
|
|
16771
|
+
let mimeType = 'image/png';
|
|
16772
|
+
if (outputFormat === 'webp') {
|
|
16773
|
+
mimeType = 'image/webp';
|
|
16774
|
+
} else if (outputFormat === 'jpeg') {
|
|
16775
|
+
mimeType = 'image/jpeg';
|
|
16776
|
+
}
|
|
16777
|
+
|
|
16778
|
+
// 第一次压缩
|
|
16779
|
+
let blob = await canvas.convertToBlob({
|
|
16780
|
+
type: mimeType,
|
|
16781
|
+
quality: finalQuality
|
|
16782
|
+
});
|
|
16783
|
+
|
|
16784
|
+
compressedDataUrl = await blobToDataURL(blob);
|
|
16785
|
+
|
|
16786
|
+
// 8. 如果压缩后文件仍然较大,尝试进一步降低质量
|
|
16787
|
+
const originalSize = dataUrl.length;
|
|
16788
|
+
let compressedSize = compressedDataUrl.length;
|
|
16789
|
+
|
|
16790
|
+
// 如果压缩后大小减少不足 20%,尝试更激进的压缩
|
|
16791
|
+
if (compressedSize > originalSize * 0.8 && finalQuality > 0.1 && (outputFormat === 'webp' || outputFormat === 'jpeg')) {
|
|
16792
|
+
const aggressiveQuality = Math.max(0.1, finalQuality * 0.7);
|
|
16793
|
+
|
|
16794
|
+
try {
|
|
16795
|
+
const moreCompressedBlob = await canvas.convertToBlob({
|
|
16796
|
+
type: mimeType,
|
|
16797
|
+
quality: aggressiveQuality
|
|
16798
|
+
});
|
|
16799
|
+
|
|
16800
|
+
const moreCompressedDataUrl = await blobToDataURL(moreCompressedBlob);
|
|
16801
|
+
|
|
16802
|
+
// 如果更激进的压缩效果更好,使用它
|
|
16803
|
+
if (moreCompressedDataUrl.length < compressedDataUrl.length) {
|
|
16804
|
+
compressedDataUrl = moreCompressedDataUrl;
|
|
16805
|
+
compressedSize = compressedDataUrl.length;
|
|
16806
|
+
}
|
|
16807
|
+
} catch (e) {
|
|
16808
|
+
// 忽略错误,使用之前的压缩结果
|
|
16809
|
+
}
|
|
16810
|
+
}
|
|
16811
|
+
|
|
16812
|
+
// 9. 返回压缩结果
|
|
16813
|
+
self.postMessage({
|
|
16814
|
+
type: 'SCREENSHOT_RESULT',
|
|
16815
|
+
data: {
|
|
16816
|
+
compressed: {
|
|
16817
|
+
dataUrl: compressedDataUrl,
|
|
16818
|
+
originalSize: originalSize,
|
|
16819
|
+
compressedSize: compressedDataUrl.length,
|
|
16820
|
+
compressionRatio: ((1 - compressedDataUrl.length / originalSize) * 100).toFixed(1)
|
|
16821
|
+
}
|
|
16822
|
+
}
|
|
16823
|
+
});
|
|
16824
|
+
} catch (error) {
|
|
16825
|
+
// 压缩失败,返回原始数据
|
|
16826
|
+
self.postMessage({
|
|
16827
|
+
type: 'SCREENSHOT_RESULT',
|
|
16828
|
+
data: {
|
|
16829
|
+
compressed: {
|
|
16830
|
+
dataUrl: data.dataUrl,
|
|
16831
|
+
error: error.message
|
|
16832
|
+
}
|
|
16833
|
+
}
|
|
16834
|
+
});
|
|
16835
|
+
}
|
|
16711
16836
|
}
|
|
16712
16837
|
};
|
|
16838
|
+
|
|
16839
|
+
// 加载图片的辅助函数(Worker 中使用 createImageBitmap)
|
|
16840
|
+
async function loadImage(dataUrl) {
|
|
16841
|
+
try {
|
|
16842
|
+
// 将 data URL 转换为 Blob
|
|
16843
|
+
let blob;
|
|
16844
|
+
if (typeof fetch !== 'undefined') {
|
|
16845
|
+
try {
|
|
16846
|
+
const response = await fetch(dataUrl);
|
|
16847
|
+
blob = await response.blob();
|
|
16848
|
+
} catch (e) {
|
|
16849
|
+
// fetch 失败,手动转换 data URL 到 Blob
|
|
16850
|
+
blob = dataURLToBlob(dataUrl);
|
|
16851
|
+
}
|
|
16852
|
+
} else {
|
|
16853
|
+
// 没有 fetch,手动转换
|
|
16854
|
+
blob = dataURLToBlob(dataUrl);
|
|
16855
|
+
}
|
|
16856
|
+
|
|
16857
|
+
// 使用 createImageBitmap 创建图片(Worker 中推荐方式)
|
|
16858
|
+
if (typeof createImageBitmap !== 'undefined') {
|
|
16859
|
+
return await createImageBitmap(blob);
|
|
16860
|
+
} else {
|
|
16861
|
+
// 回退方案:使用 Image(某些 Worker 环境可能支持)
|
|
16862
|
+
return new Promise((resolve, reject) => {
|
|
16863
|
+
const img = new Image();
|
|
16864
|
+
img.onload = () => resolve(img);
|
|
16865
|
+
img.onerror = reject;
|
|
16866
|
+
img.src = dataUrl;
|
|
16867
|
+
});
|
|
16868
|
+
}
|
|
16869
|
+
} catch (error) {
|
|
16870
|
+
// 如果 createImageBitmap 失败,尝试直接使用 Image
|
|
16871
|
+
return new Promise((resolve, reject) => {
|
|
16872
|
+
const img = new Image();
|
|
16873
|
+
img.onload = () => resolve(img);
|
|
16874
|
+
img.onerror = () => reject(error);
|
|
16875
|
+
img.src = dataUrl;
|
|
16876
|
+
});
|
|
16877
|
+
}
|
|
16878
|
+
}
|
|
16879
|
+
|
|
16880
|
+
// 将 data URL 转换为 Blob 的辅助函数
|
|
16881
|
+
function dataURLToBlob(dataUrl) {
|
|
16882
|
+
const arr = dataUrl.split(',');
|
|
16883
|
+
const mimeMatch = arr[0].match(/:(.*?);/);
|
|
16884
|
+
const mime = mimeMatch ? mimeMatch[1] : 'image/png';
|
|
16885
|
+
const bstr = atob(arr[1]);
|
|
16886
|
+
let n = bstr.length;
|
|
16887
|
+
const u8arr = new Uint8Array(n);
|
|
16888
|
+
while (n--) {
|
|
16889
|
+
u8arr[n] = bstr.charCodeAt(n);
|
|
16890
|
+
}
|
|
16891
|
+
return new Blob([u8arr], { type: mime });
|
|
16892
|
+
}
|
|
16893
|
+
|
|
16894
|
+
// 计算压缩后尺寸的辅助函数(保持宽高比)
|
|
16895
|
+
function calculateSize(originalWidth, originalHeight, maxWidth, maxHeight) {
|
|
16896
|
+
let width = originalWidth;
|
|
16897
|
+
let height = originalHeight;
|
|
16898
|
+
|
|
16899
|
+
if (width > maxWidth || height > maxHeight) {
|
|
16900
|
+
const widthRatio = maxWidth / width;
|
|
16901
|
+
const heightRatio = maxHeight / height;
|
|
16902
|
+
const ratio = Math.min(widthRatio, heightRatio);
|
|
16903
|
+
width = Math.round(width * ratio);
|
|
16904
|
+
height = Math.round(height * ratio);
|
|
16905
|
+
}
|
|
16906
|
+
|
|
16907
|
+
return { width, height };
|
|
16908
|
+
}
|
|
16713
16909
|
`;
|
|
16714
16910
|
const blob = new Blob([workerCode], { type: 'application/javascript' });
|
|
16715
16911
|
const workerUrl = URL.createObjectURL(blob);
|
|
@@ -16717,17 +16913,34 @@ class ScreenshotManager {
|
|
|
16717
16913
|
newWorker.onmessage = (e) => {
|
|
16718
16914
|
const { type, data } = e.data;
|
|
16719
16915
|
if (type === 'SCREENSHOT_RESULT' && data?.compressed) {
|
|
16916
|
+
const compressed = data.compressed;
|
|
16917
|
+
// 更新截图历史记录
|
|
16720
16918
|
if (this.screenshotHistory.length > 0) {
|
|
16721
|
-
this.screenshotHistory[this.screenshotHistory.length - 1] =
|
|
16919
|
+
this.screenshotHistory[this.screenshotHistory.length - 1] = compressed.dataUrl;
|
|
16920
|
+
// 打印压缩统计信息
|
|
16921
|
+
if (!this.options.silentMode && compressed.originalSize && compressed.compressedSize) {
|
|
16922
|
+
const originalKB = (compressed.originalSize * 0.75 / 1024).toFixed(2);
|
|
16923
|
+
const compressedKB = (compressed.compressedSize * 0.75 / 1024).toFixed(2);
|
|
16924
|
+
const ratio = compressed.compressionRatio || '0';
|
|
16925
|
+
console.log('📸 [Worker 压缩] ✅ 压缩完成');
|
|
16926
|
+
console.log(` 原始大小: ${originalKB} KB`);
|
|
16927
|
+
console.log(` 压缩后: ${compressedKB} KB`);
|
|
16928
|
+
console.log(` 压缩率: ${ratio}%`);
|
|
16929
|
+
if (compressed.error) {
|
|
16930
|
+
console.warn(` ⚠️ 压缩警告: ${compressed.error}`);
|
|
16931
|
+
}
|
|
16932
|
+
}
|
|
16722
16933
|
}
|
|
16723
16934
|
}
|
|
16724
16935
|
};
|
|
16725
16936
|
newWorker.onerror = (e) => {
|
|
16726
16937
|
console.error('📸 WebWorker 错误:', e);
|
|
16938
|
+
if (!this.options.silentMode) {
|
|
16939
|
+
console.warn('📸 Worker 压缩失败,使用原始截图');
|
|
16940
|
+
}
|
|
16727
16941
|
};
|
|
16728
16942
|
// 注意:不要立即 revokeObjectURL,因为 Worker 需要这个 URL 保持有效
|
|
16729
16943
|
// 在 destroy() 方法中清理 Worker 时再 revoke
|
|
16730
|
-
// URL.revokeObjectURL(workerUrl) // 已移除,在 destroy 时清理
|
|
16731
16944
|
return newWorker;
|
|
16732
16945
|
}
|
|
16733
16946
|
catch (err) {
|