Skip to content

Commit bac1d7b

Browse files
QxQstarJuice10YunFeng0817Mark-Fenng
authored
feat: add dataURLOptions parameter control canvas image format and quality (#967)
* feat: record add dataURLOptions parameter control canvas image format and quality * 解决 build failed * Update docs/recipes/canvas.md * Apply formatting changes * Update canvas-manager.ts Fix the error caused when I resolved the merge conflict Co-authored-by: Justin Halsall <[email protected]> Co-authored-by: Yun Feng <[email protected]> Co-authored-by: Mark-Fenng <[email protected]> Co-authored-by: Yun Feng <[email protected]>
1 parent 96b7466 commit bac1d7b

File tree

9 files changed

+45
-6
lines changed

9 files changed

+45
-6
lines changed

docs/recipes/canvas.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ rrweb.record({
2121
sampling: {
2222
canvas: 15,
2323
},
24+
// optional image format settings
25+
dataURLOptions: {
26+
type: 'image/webp',
27+
quality: 0.6,
28+
},
2429
});
2530
```
2631

docs/recipes/canvas.zh_CN.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ rrweb.record({
2121
sampling: {
2222
canvas: 15,
2323
},
24+
// 图像的格式
25+
dataURLOptions: {
26+
type: 'image/webp',
27+
quality: 0.6,
28+
},
2429
});
2530
```
2631

guide.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ The parameter of `rrweb.record` accepts the following options.
151151
| maskInputFn | - | customize mask input content recording logic |
152152
| maskTextFn | - | customize mask text content recording logic |
153153
| slimDOMOptions | {} | remove unnecessary parts of the DOM <br />refer to the [list](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L97-L108) |
154+
| dataURLOptions | {} | Canvas image format and quality ,This parameter will be passed to the OffscreenCanvas.convertToBlob(),Using this parameter effectively reduces the size of the recorded data |
154155
| inlineStylesheet | true | whether to inline the stylesheet in the events |
155156
| hooks | {} | hooks for events<br />refer to the [list](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L207) |
156157
| packFn | - | refer to the [storage optimization recipe](./docs/recipes/optimize-storage.md) |

guide.zh_CN.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ setInterval(save, 10 * 1000);
151151
| hooks | {} | 各类事件的回调<br />类型详见[列表](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L207) |
152152
| packFn | - | 数据压缩函数,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) |
153153
| sampling | - | 数据抽样策略,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) |
154+
| dataURLOptions | {} | Canvas 图像快照的格式和质量,这个参数将传递给 OffscreenCanvas.convertToBlob(),使用这个参数能有效减小录制数据的大小 |
154155
| recordCanvas | false | 是否记录 canvas 内容, 可用选项:false, true |
155156
| inlineImages | false | 是否将图片内容记内联录制 |
156157
| collectFonts | false | 是否记录页面中的字体文件 |

packages/rrweb/src/record/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ function record<T = eventWithTime>(
6464
hooks,
6565
packFn,
6666
sampling = {},
67+
dataURLOptions = {},
6768
mousemoveWait,
6869
recordCanvas = false,
6970
userTriggeredOnInput = false,
@@ -240,6 +241,7 @@ function record<T = eventWithTime>(
240241
blockSelector,
241242
mirror,
242243
sampling: sampling.canvas,
244+
dataURLOptions,
243245
});
244246

245247
const shadowDomManager = new ShadowDomManager({
@@ -252,6 +254,7 @@ function record<T = eventWithTime>(
252254
maskTextSelector,
253255
inlineStylesheet,
254256
maskInputOptions,
257+
dataURLOptions,
255258
maskTextFn,
256259
maskInputFn,
257260
recordCanvas,
@@ -290,6 +293,7 @@ function record<T = eventWithTime>(
290293
maskAllInputs: maskInputOptions,
291294
maskTextFn,
292295
slimDOM: slimDOMOptions,
296+
dataURLOptions,
293297
recordCanvas,
294298
inlineImages,
295299
onSerialize: (n) => {
@@ -471,6 +475,7 @@ function record<T = eventWithTime>(
471475
keepIframeSrcFn,
472476
blockSelector,
473477
slimDOMOptions,
478+
dataURLOptions,
474479
mirror,
475480
iframeManager,
476481
stylesheetManager,

packages/rrweb/src/record/mutation.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ export default class MutationBuffer {
169169
private recordCanvas: observerParam['recordCanvas'];
170170
private inlineImages: observerParam['inlineImages'];
171171
private slimDOMOptions: observerParam['slimDOMOptions'];
172+
private dataURLOptions: observerParam['dataURLOptions'];
172173
private doc: observerParam['doc'];
173174
private mirror: observerParam['mirror'];
174175
private iframeManager: observerParam['iframeManager'];
@@ -191,6 +192,7 @@ export default class MutationBuffer {
191192
'recordCanvas',
192193
'inlineImages',
193194
'slimDOMOptions',
195+
'dataURLOptions',
194196
'doc',
195197
'mirror',
196198
'iframeManager',
@@ -301,6 +303,7 @@ export default class MutationBuffer {
301303
maskTextFn: this.maskTextFn,
302304
maskInputFn: this.maskInputFn,
303305
slimDOMOptions: this.slimDOMOptions,
306+
dataURLOptions: this.dataURLOptions,
304307
recordCanvas: this.recordCanvas,
305308
inlineImages: this.inlineImages,
306309
onSerialize: (currentN) => {

packages/rrweb/src/record/observers/canvas/canvas-manager.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { ICanvas, Mirror } from 'rrweb-snapshot';
1+
import type { ICanvas, Mirror, DataURLOptions } from 'rrweb-snapshot';
22
import type {
33
blockClass,
44
canvasManagerMutationCallback,
@@ -63,21 +63,25 @@ export class CanvasManager {
6363
blockSelector: string | null;
6464
mirror: Mirror;
6565
sampling?: 'all' | number;
66+
dataURLOptions: DataURLOptions;
6667
}) {
6768
const {
6869
sampling = 'all',
6970
win,
7071
blockClass,
7172
blockSelector,
7273
recordCanvas,
74+
dataURLOptions,
7375
} = options;
7476
this.mutationCb = options.mutationCb;
7577
this.mirror = options.mirror;
7678

7779
if (recordCanvas && sampling === 'all')
7880
this.initCanvasMutationObserver(win, blockClass, blockSelector);
7981
if (recordCanvas && typeof sampling === 'number')
80-
this.initCanvasFPSObserver(sampling, win, blockClass, blockSelector);
82+
this.initCanvasFPSObserver(sampling, win, blockClass, blockSelector, {
83+
dataURLOptions,
84+
});
8185
}
8286

8387
private processMutation: canvasManagerMutationCallback = (
@@ -102,6 +106,9 @@ export class CanvasManager {
102106
win: IWindow,
103107
blockClass: blockClass,
104108
blockSelector: string | null,
109+
options: {
110+
dataURLOptions: DataURLOptions;
111+
},
105112
) {
106113
const canvasContextReset = initCanvasContextObserver(
107114
win,
@@ -202,6 +209,7 @@ export class CanvasManager {
202209
bitmap,
203210
width: canvas.width,
204211
height: canvas.height,
212+
dataURLOptions: options.dataURLOptions,
205213
},
206214
[bitmap],
207215
);

packages/rrweb/src/record/workers/image-bitmap-data-url-worker.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { encode } from 'base64-arraybuffer';
2+
import type { DataURLOptions } from 'rrweb-snapshot';
23
import type {
34
ImageBitmapDataURLWorkerParams,
45
ImageBitmapDataURLWorkerResponse,
@@ -25,12 +26,13 @@ interface ImageBitmapDataURLResponseWorker {
2526
async function getTransparentBlobFor(
2627
width: number,
2728
height: number,
29+
dataURLOptions: DataURLOptions,
2830
): Promise<string> {
2931
const id = `${width}-${height}`;
3032
if (transparentBlobMap.has(id)) return transparentBlobMap.get(id)!;
3133
const offscreen = new OffscreenCanvas(width, height);
3234
offscreen.getContext('2d'); // creates rendering context for `converToBlob`
33-
const blob = await offscreen.convertToBlob(); // takes a while
35+
const blob = await offscreen.convertToBlob(dataURLOptions); // takes a while
3436
const arrayBuffer = await blob.arrayBuffer();
3537
const base64 = encode(arrayBuffer); // cpu intensive
3638
transparentBlobMap.set(id, base64);
@@ -45,16 +47,20 @@ worker.onmessage = async function (e) {
4547
if (!('OffscreenCanvas' in globalThis))
4648
return worker.postMessage({ id: e.data.id });
4749

48-
const { id, bitmap, width, height } = e.data;
50+
const { id, bitmap, width, height, dataURLOptions } = e.data;
4951

50-
const transparentBase64 = getTransparentBlobFor(width, height);
52+
const transparentBase64 = getTransparentBlobFor(
53+
width,
54+
height,
55+
dataURLOptions,
56+
);
5157

5258
const offscreen = new OffscreenCanvas(width, height);
5359
const ctx = offscreen.getContext('2d')!;
5460

5561
ctx.drawImage(bitmap, 0, 0);
5662
bitmap.close();
57-
const blob = await offscreen.convertToBlob(); // takes a while
63+
const blob = await offscreen.convertToBlob(dataURLOptions); // takes a while
5864
const type = blob.type;
5965
const arrayBuffer = await blob.arrayBuffer();
6066
const base64 = encode(arrayBuffer); // cpu intensive

packages/rrweb/src/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {
66
SlimDOMOptions,
77
MaskInputFn,
88
MaskTextFn,
9+
DataURLOptions,
910
} from 'rrweb-snapshot';
1011
import type { PackFn, UnpackFn } from './packer/base';
1112
import type { IframeManager } from './record/iframe-manager';
@@ -251,6 +252,7 @@ export type recordOptions<T> = {
251252
hooks?: hooksParam;
252253
packFn?: PackFn;
253254
sampling?: SamplingStrategy;
255+
dataURLOptions?: DataURLOptions;
254256
recordCanvas?: boolean;
255257
userTriggeredOnInput?: boolean;
256258
collectFonts?: boolean;
@@ -290,6 +292,7 @@ export type observerParam = {
290292
userTriggeredOnInput: boolean;
291293
collectFonts: boolean;
292294
slimDOMOptions: SlimDOMOptions;
295+
dataURLOptions: DataURLOptions;
293296
doc: Document;
294297
mirror: Mirror;
295298
iframeManager: IframeManager;
@@ -323,6 +326,7 @@ export type MutationBufferParam = Pick<
323326
| 'recordCanvas'
324327
| 'inlineImages'
325328
| 'slimDOMOptions'
329+
| 'dataURLOptions'
326330
| 'doc'
327331
| 'mirror'
328332
| 'iframeManager'
@@ -563,6 +567,7 @@ export type ImageBitmapDataURLWorkerParams = {
563567
bitmap: ImageBitmap;
564568
width: number;
565569
height: number;
570+
dataURLOptions: DataURLOptions;
566571
};
567572

568573
export type ImageBitmapDataURLWorkerResponse =

0 commit comments

Comments
 (0)