Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/recipes/canvas.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ rrweb.record({
sampling: {
canvas: 15,
},
// optional image format settings
dataURLOptions: {
type: 'image/webp',
quality: 0.6,
},
});
```

Expand Down
5 changes: 5 additions & 0 deletions docs/recipes/canvas.zh_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ rrweb.record({
sampling: {
canvas: 15,
},
// 图像的格式
dataURLOptions: {
type: 'image/webp',
quality: 0.6,
},
});
```

Expand Down
1 change: 1 addition & 0 deletions guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ The parameter of `rrweb.record` accepts the following options.
| maskInputFn | - | customize mask input content recording logic |
| maskTextFn | - | customize mask text content recording logic |
| 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) |
| 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 |
| inlineStylesheet | true | whether to inline the stylesheet in the events |
| hooks | {} | hooks for events<br />refer to the [list](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L207) |
| packFn | - | refer to the [storage optimization recipe](./docs/recipes/optimize-storage.md) |
Expand Down
1 change: 1 addition & 0 deletions guide.zh_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ setInterval(save, 10 * 1000);
| hooks | {} | 各类事件的回调<br />类型详见[列表](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L207) |
| packFn | - | 数据压缩函数,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) |
| sampling | - | 数据抽样策略,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) |
| dataURLOptions | {} | Canvas 图像快照的格式和质量,这个参数将传递给 OffscreenCanvas.convertToBlob(),使用这个参数能有效减小录制数据的大小 |
| recordCanvas | false | 是否记录 canvas 内容, 可用选项:false, true |
| inlineImages | false | 是否将图片内容记内联录制 |
| collectFonts | false | 是否记录页面中的字体文件 |
Expand Down
5 changes: 5 additions & 0 deletions packages/rrweb/src/record/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ function record<T = eventWithTime>(
hooks,
packFn,
sampling = {},
dataURLOptions = {},
mousemoveWait,
recordCanvas = false,
userTriggeredOnInput = false,
Expand Down Expand Up @@ -240,6 +241,7 @@ function record<T = eventWithTime>(
blockSelector,
mirror,
sampling: sampling.canvas,
dataURLOptions,
});

const shadowDomManager = new ShadowDomManager({
Expand All @@ -252,6 +254,7 @@ function record<T = eventWithTime>(
maskTextSelector,
inlineStylesheet,
maskInputOptions,
dataURLOptions,
maskTextFn,
maskInputFn,
recordCanvas,
Expand Down Expand Up @@ -290,6 +293,7 @@ function record<T = eventWithTime>(
maskAllInputs: maskInputOptions,
maskTextFn,
slimDOM: slimDOMOptions,
dataURLOptions,
recordCanvas,
inlineImages,
onSerialize: (n) => {
Expand Down Expand Up @@ -471,6 +475,7 @@ function record<T = eventWithTime>(
keepIframeSrcFn,
blockSelector,
slimDOMOptions,
dataURLOptions,
mirror,
iframeManager,
stylesheetManager,
Expand Down
3 changes: 3 additions & 0 deletions packages/rrweb/src/record/mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export default class MutationBuffer {
private recordCanvas: observerParam['recordCanvas'];
private inlineImages: observerParam['inlineImages'];
private slimDOMOptions: observerParam['slimDOMOptions'];
private dataURLOptions: observerParam['dataURLOptions'];
private doc: observerParam['doc'];
private mirror: observerParam['mirror'];
private iframeManager: observerParam['iframeManager'];
Expand All @@ -191,6 +192,7 @@ export default class MutationBuffer {
'recordCanvas',
'inlineImages',
'slimDOMOptions',
'dataURLOptions',
'doc',
'mirror',
'iframeManager',
Expand Down Expand Up @@ -301,6 +303,7 @@ export default class MutationBuffer {
maskTextFn: this.maskTextFn,
maskInputFn: this.maskInputFn,
slimDOMOptions: this.slimDOMOptions,
dataURLOptions: this.dataURLOptions,
recordCanvas: this.recordCanvas,
inlineImages: this.inlineImages,
onSerialize: (currentN) => {
Expand Down
12 changes: 10 additions & 2 deletions packages/rrweb/src/record/observers/canvas/canvas-manager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ICanvas, Mirror } from 'rrweb-snapshot';
import type { ICanvas, Mirror, DataURLOptions } from 'rrweb-snapshot';
import type {
blockClass,
canvasManagerMutationCallback,
Expand Down Expand Up @@ -63,21 +63,25 @@ export class CanvasManager {
blockSelector: string | null;
mirror: Mirror;
sampling?: 'all' | number;
dataURLOptions: DataURLOptions;
}) {
const {
sampling = 'all',
win,
blockClass,
blockSelector,
recordCanvas,
dataURLOptions,
} = options;
this.mutationCb = options.mutationCb;
this.mirror = options.mirror;

if (recordCanvas && sampling === 'all')
this.initCanvasMutationObserver(win, blockClass, blockSelector);
if (recordCanvas && typeof sampling === 'number')
this.initCanvasFPSObserver(sampling, win, blockClass, blockSelector);
this.initCanvasFPSObserver(sampling, win, blockClass, blockSelector, {
dataURLOptions,
});
}

private processMutation: canvasManagerMutationCallback = (
Expand All @@ -102,6 +106,9 @@ export class CanvasManager {
win: IWindow,
blockClass: blockClass,
blockSelector: string | null,
options: {
dataURLOptions: DataURLOptions;
},
) {
const canvasContextReset = initCanvasContextObserver(
win,
Expand Down Expand Up @@ -202,6 +209,7 @@ export class CanvasManager {
bitmap,
width: canvas.width,
height: canvas.height,
dataURLOptions: options.dataURLOptions,
},
[bitmap],
);
Expand Down
14 changes: 10 additions & 4 deletions packages/rrweb/src/record/workers/image-bitmap-data-url-worker.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { encode } from 'base64-arraybuffer';
import type { DataURLOptions } from 'rrweb-snapshot';
import type {
ImageBitmapDataURLWorkerParams,
ImageBitmapDataURLWorkerResponse,
Expand All @@ -25,12 +26,13 @@ interface ImageBitmapDataURLResponseWorker {
async function getTransparentBlobFor(
width: number,
height: number,
dataURLOptions: DataURLOptions,
): Promise<string> {
const id = `${width}-${height}`;
if (transparentBlobMap.has(id)) return transparentBlobMap.get(id)!;
const offscreen = new OffscreenCanvas(width, height);
offscreen.getContext('2d'); // creates rendering context for `converToBlob`
const blob = await offscreen.convertToBlob(); // takes a while
const blob = await offscreen.convertToBlob(dataURLOptions); // takes a while
const arrayBuffer = await blob.arrayBuffer();
const base64 = encode(arrayBuffer); // cpu intensive
transparentBlobMap.set(id, base64);
Expand All @@ -45,16 +47,20 @@ worker.onmessage = async function (e) {
if (!('OffscreenCanvas' in globalThis))
return worker.postMessage({ id: e.data.id });

const { id, bitmap, width, height } = e.data;
const { id, bitmap, width, height, dataURLOptions } = e.data;

const transparentBase64 = getTransparentBlobFor(width, height);
const transparentBase64 = getTransparentBlobFor(
width,
height,
dataURLOptions,
);

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

ctx.drawImage(bitmap, 0, 0);
bitmap.close();
const blob = await offscreen.convertToBlob(); // takes a while
const blob = await offscreen.convertToBlob(dataURLOptions); // takes a while
const type = blob.type;
const arrayBuffer = await blob.arrayBuffer();
const base64 = encode(arrayBuffer); // cpu intensive
Expand Down
5 changes: 5 additions & 0 deletions packages/rrweb/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
SlimDOMOptions,
MaskInputFn,
MaskTextFn,
DataURLOptions,
} from 'rrweb-snapshot';
import type { PackFn, UnpackFn } from './packer/base';
import type { IframeManager } from './record/iframe-manager';
Expand Down Expand Up @@ -251,6 +252,7 @@ export type recordOptions<T> = {
hooks?: hooksParam;
packFn?: PackFn;
sampling?: SamplingStrategy;
dataURLOptions?: DataURLOptions;
recordCanvas?: boolean;
userTriggeredOnInput?: boolean;
collectFonts?: boolean;
Expand Down Expand Up @@ -290,6 +292,7 @@ export type observerParam = {
userTriggeredOnInput: boolean;
collectFonts: boolean;
slimDOMOptions: SlimDOMOptions;
dataURLOptions: DataURLOptions;
doc: Document;
mirror: Mirror;
iframeManager: IframeManager;
Expand Down Expand Up @@ -323,6 +326,7 @@ export type MutationBufferParam = Pick<
| 'recordCanvas'
| 'inlineImages'
| 'slimDOMOptions'
| 'dataURLOptions'
| 'doc'
| 'mirror'
| 'iframeManager'
Expand Down Expand Up @@ -563,6 +567,7 @@ export type ImageBitmapDataURLWorkerParams = {
bitmap: ImageBitmap;
width: number;
height: number;
dataURLOptions: DataURLOptions;
};

export type ImageBitmapDataURLWorkerResponse =
Expand Down