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
4 changes: 4 additions & 0 deletions guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,9 @@ replayer.pause();

// pause at the fifth seconds
replayer.pause(5000);

// destroy the replayer (hint: this operation is irreversible)
replayer.destroy();
```

#### Options
Expand Down Expand Up @@ -385,6 +388,7 @@ The event list:
| mouse-interaction | mouse interaction has been replayed | { type, target } |
| event-cast | event has been replayed | event |
| custom-event | custom event has been replayed | event |
| destroy | destroyed the replayer | - |

The rrweb-replayer also re-expose the event listener via a `component.addEventListener` API.

Expand Down
4 changes: 4 additions & 0 deletions guide.zh_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,9 @@ replayer.pause();

// 暂停至第 5 秒处
replayer.pause(5000);

// 销毁播放器 (提示: 这个操作不可逆)
replayer.destroy();
```

#### 配置参数
Expand Down Expand Up @@ -384,6 +387,7 @@ replayer.on(EVENT_NAME, (payload) => {
| mouse-interaction | 回放鼠标交互事件 | { type, target } |
| event-cast | 回放 event | event |
| custom-event | 回放自定义事件 | event |
| destroy | 销毁播放器 | - |

使用 `rrweb-player` 时,也可以通过 `addEventListener` API 使用相同的事件功能,并且会获得 3 个额外的事件:

Expand Down
29 changes: 21 additions & 8 deletions packages/rrweb/src/replay/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ export class Replayer {
this.rebuildFullSnapshot(
firstFullsnapshot as fullSnapshotEvent & { timestamp: number },
);
this.iframe.contentWindow!.scrollTo(
this.iframe.contentWindow?.scrollTo(
(firstFullsnapshot as fullSnapshotEvent).data.initialOffset,
);
}, 1);
Expand Down Expand Up @@ -433,12 +433,22 @@ export class Replayer {

public resume(timeOffset = 0) {
console.warn(
`The 'resume' will be departed in 1.0. Please use 'play' method which has the same interface.`,
`The 'resume' was deprecated in 1.0. Please use 'play' method which has the same interface.`,
);
this.play(timeOffset);
this.emitter.emit(ReplayerEvents.Resume);
}

/**
* Totally destroy this replayer and please be careful that this operation is irreversible.
* Memory occupation can be released by removing all references to this replayer.
*/
public destroy() {
this.pause();
this.config.root.removeChild(this.wrapper);
this.emitter.emit(ReplayerEvents.Destroy);
}

public startLive(baselineTime?: number) {
this.service.send({ type: 'TO_LIVE', payload: { baselineTime } });
}
Expand Down Expand Up @@ -582,7 +592,7 @@ export class Replayer {
this.firstFullSnapshot = true;
}
this.rebuildFullSnapshot(event, isSync);
this.iframe.contentWindow!.scrollTo(event.data.initialOffset);
this.iframe.contentWindow?.scrollTo(event.data.initialOffset);
};
break;
case EventType.IncrementalSnapshot:
Expand All @@ -603,6 +613,7 @@ export class Replayer {
}
if (this.isUserInteraction(_event)) {
if (
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
_event.delay! - event.delay! >
SKIP_TIME_THRESHOLD *
this.speedService.state.context.timer.speed
Expand All @@ -614,6 +625,7 @@ export class Replayer {
}
if (this.nextUserInteractionEvent) {
const skipTime =
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.nextUserInteractionEvent.delay! - event.delay!;
const payload = {
speed: Math.min(
Expand Down Expand Up @@ -734,7 +746,7 @@ export class Replayer {
styleEl,
getDefaultSN(styleEl, this.virtualDom.unserializedId),
);
(documentElement as RRElement)!.insertBefore(styleEl, head as RRElement);
(documentElement as RRElement).insertBefore(styleEl, head as RRElement);
for (let idx = 0; idx < injectStylesRules.length; idx++) {
// push virtual styles
styleEl.rules.push({
Expand All @@ -745,12 +757,12 @@ export class Replayer {
}
} else {
const styleEl = document.createElement('style');
(documentElement as HTMLElement)!.insertBefore(
(documentElement as HTMLElement).insertBefore(
styleEl,
head as HTMLHeadElement,
);
for (let idx = 0; idx < injectStylesRules.length; idx++) {
styleEl.sheet!.insertRule(injectStylesRules[idx], idx);
styleEl.sheet?.insertRule(injectStylesRules[idx], idx);
}
}
}
Expand Down Expand Up @@ -984,6 +996,7 @@ export class Replayer {
doAction() {
//
},
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
delay: e.delay! - d.positions[0]?.timeOffset,
});
}
Expand Down Expand Up @@ -1721,14 +1734,14 @@ export class Replayer {
}
const sn = this.mirror.getMeta(target);
if (target === this.iframe.contentDocument) {
this.iframe.contentWindow!.scrollTo({
this.iframe.contentWindow?.scrollTo({
top: d.y,
left: d.x,
behavior: isSync ? 'auto' : 'smooth',
});
} else if (sn?.type === NodeType.Document) {
// nest iframe content document
(target as Document).defaultView!.scrollTo({
(target as Document).defaultView?.scrollTo({
top: d.y,
left: d.x,
behavior: isSync ? 'auto' : 'smooth',
Expand Down
1 change: 1 addition & 0 deletions packages/rrweb/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,7 @@ export enum ReplayerEvents {
Flush = 'flush',
StateChange = 'state-change',
PlayBack = 'play-back',
Destroy = 'destroy',
}

export type KeepIframeSrcFn = (src: string) => boolean;
Expand Down
17 changes: 17 additions & 0 deletions packages/rrweb/test/replayer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -687,4 +687,21 @@ describe('replayer', function () {

await assertDomSnapshot(page);
});

it('should destroy the replayer after calling destroy()', async () => {
await page.evaluate(`events = ${JSON.stringify(events)}`);
await page.evaluate(`
const { Replayer } = rrweb;
let replayer = new Replayer(events);
replayer.play();
`);

const replayerWrapperClassName = 'replayer-wrapper';
let wrapper = await page.$(`.${replayerWrapperClassName}`);
expect(wrapper).not.toBeNull();

await page.evaluate(`replayer.destroy(); replayer = null;`);
wrapper = await page.$(`.${replayerWrapperClassName}`);
expect(wrapper).toBeNull();
});
});