impl dynamic config and pause. Bump 0.3.0

This commit is contained in:
Yanzhen Yu
2026-04-01 12:00:00 +08:00
parent e040ae76c5
commit f75748674f
4 changed files with 46 additions and 9 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "rrweb", "name": "rrweb",
"version": "0.2.0", "version": "0.3.0",
"description": "record and replay the web", "description": "record and replay the web",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@@ -1,5 +1,5 @@
import { rebuild, serializeNodeWithId } from 'rrweb-snapshot'; import { rebuild, serializeNodeWithId } from 'rrweb-snapshot';
import later from './timer'; import { later, clear } from './timer';
import { import {
EventType, EventType,
incrementalData, incrementalData,
@@ -7,18 +7,35 @@ import {
fullSnapshotEvent, fullSnapshotEvent,
eventWithTime, eventWithTime,
MouseInteractions, MouseInteractions,
playerConfig,
} from '../types'; } from '../types';
import { mirror, getIdNodeMap } from '../utils'; import { mirror, getIdNodeMap } from '../utils';
const defaultConfig: playerConfig = {
speed: 1,
};
export class Replayer { export class Replayer {
private events: eventWithTime[] = []; private events: eventWithTime[] = [];
private config: playerConfig;
private wrapper: HTMLDivElement; private wrapper: HTMLDivElement;
private iframe: HTMLIFrameElement; private iframe: HTMLIFrameElement;
private mouse: HTMLDivElement; private mouse: HTMLDivElement;
private startTime: number = 0; private startTime: number = 0;
constructor(events: eventWithTime[]) { private timerIds: number[] = [];
constructor(events: eventWithTime[], config: playerConfig = defaultConfig) {
this.events = events; this.events = events;
this.config = config;
}
public setConfig(config: Partial<playerConfig>) {
this.config = {
...this.config,
...config,
};
} }
public play() { public play() {
@@ -33,12 +50,13 @@ export class Replayer {
this.iframe.height = `${event.data.height}px`; this.iframe.height = `${event.data.height}px`;
break; break;
case EventType.FullSnapshot: case EventType.FullSnapshot:
later(() => { this.later(() => {
this.rebuildFullSnapshot(event); this.rebuildFullSnapshot(event);
this.iframe.contentWindow!.scrollTo(event.data.initialOffset);
}, this.getDelay(event)); }, this.getDelay(event));
break; break;
case EventType.IncrementalSnapshot: case EventType.IncrementalSnapshot:
later(() => { this.later(() => {
this.applyIncremental(event.data); this.applyIncremental(event.data);
}, this.getDelay(event)); }, this.getDelay(event));
break; break;
@@ -47,6 +65,10 @@ export class Replayer {
} }
} }
public pause() {
this.timerIds.forEach(clear);
}
private setupDom() { private setupDom() {
this.wrapper = document.createElement('div'); this.wrapper = document.createElement('div');
this.wrapper.classList.add('replayer-wrapper'); this.wrapper.classList.add('replayer-wrapper');
@@ -60,6 +82,11 @@ export class Replayer {
this.wrapper.appendChild(this.iframe); this.wrapper.appendChild(this.iframe);
} }
private later(cb: () => void, delayMs: number) {
const id = later(cb, delayMs, this.config.speed);
this.timerIds.push(id);
}
private getDelay(event: eventWithTime): number { private getDelay(event: eventWithTime): number {
// Mouse move events was recorded in a throttle function, // Mouse move events was recorded in a throttle function,
// so we need to find the real timestamp by traverse the time offsets. // so we need to find the real timestamp by traverse the time offsets.
@@ -152,7 +179,7 @@ export class Replayer {
} }
case IncrementalSource.MouseMove: case IncrementalSource.MouseMove:
d.positions.forEach(p => { d.positions.forEach(p => {
later(() => { this.later(() => {
this.mouse.style.left = `${p.x}px`; this.mouse.style.left = `${p.x}px`;
this.mouse.style.top = `${p.y}px`; this.mouse.style.top = `${p.y}px`;
}, p.timeOffset); }, p.timeOffset);

View File

@@ -1,17 +1,27 @@
const FRAME_MS = 16; const FRAME_MS = 16;
const timerMap: Map<number, boolean> = new Map();
function later(cb: () => void, delayMs: number, speed = 1) { export function later(cb: () => void, delayMs: number, speed = 1): number {
const now = performance.now(); const now = performance.now();
const id = timerMap.size + 1;
timerMap.set(id, true);
function check(step: number) { function check(step: number) {
if (!timerMap.has(id)) {
return;
}
if (step - now > delayMs / speed - FRAME_MS) { if (step - now > delayMs / speed - FRAME_MS) {
cb(); cb();
clear(id);
} else { } else {
requestAnimationFrame(check); requestAnimationFrame(check);
} }
} }
requestAnimationFrame(check); requestAnimationFrame(check);
return id;
} }
export default later; export function clear(id: number) {
timerMap.delete(id);
}

File diff suppressed because one or more lines are too long