implement pause which has a better performance than play at some time offset
This commit is contained in:
@@ -31,11 +31,14 @@ export class Replayer {
|
|||||||
|
|
||||||
private iframe: HTMLIFrameElement;
|
private iframe: HTMLIFrameElement;
|
||||||
private mouse: HTMLDivElement;
|
private mouse: HTMLDivElement;
|
||||||
private startTime: number = 0;
|
private baselineTime: number = 0;
|
||||||
|
|
||||||
private timerIds: number[] = [];
|
private timerIds: number[] = [];
|
||||||
private emitter: mitt.Emitter = mitt();
|
private emitter: mitt.Emitter = mitt();
|
||||||
|
|
||||||
|
// record last played event timestamp when paused
|
||||||
|
private lastPlayedEvent: eventWithTime;
|
||||||
|
|
||||||
constructor(events: eventWithTime[], config?: Partial<playerConfig>) {
|
constructor(events: eventWithTime[], config?: Partial<playerConfig>) {
|
||||||
if (events.length < 2) {
|
if (events.length < 2) {
|
||||||
throw new Error('Replayer need at least 2 events.');
|
throw new Error('Replayer need at least 2 events.');
|
||||||
@@ -76,37 +79,10 @@ export class Replayer {
|
|||||||
* @param timeOffset number
|
* @param timeOffset number
|
||||||
*/
|
*/
|
||||||
public play(timeOffset = 0) {
|
public play(timeOffset = 0) {
|
||||||
this.startTime = this.events[0].timestamp + timeOffset;
|
this.baselineTime = this.events[0].timestamp + timeOffset;
|
||||||
for (const event of this.events) {
|
for (const event of this.events) {
|
||||||
const isSync = event.timestamp < this.startTime;
|
const isSync = event.timestamp < this.baselineTime;
|
||||||
let castFn: undefined | (() => void);
|
const castFn = this.getCastFn(event, isSync);
|
||||||
switch (event.type) {
|
|
||||||
case EventType.DomContentLoaded:
|
|
||||||
case EventType.Load:
|
|
||||||
break;
|
|
||||||
case EventType.Meta:
|
|
||||||
castFn = () =>
|
|
||||||
this.emitter.emit('resize', {
|
|
||||||
width: event.data.width,
|
|
||||||
height: event.data.height,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case EventType.FullSnapshot:
|
|
||||||
castFn = () => {
|
|
||||||
this.rebuildFullSnapshot(event);
|
|
||||||
this.iframe.contentWindow!.scrollTo(event.data.initialOffset);
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case EventType.IncrementalSnapshot:
|
|
||||||
castFn = () => {
|
|
||||||
this.applyIncremental(event.data, isSync);
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
if (!castFn) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (isSync) {
|
if (isSync) {
|
||||||
castFn();
|
castFn();
|
||||||
} else {
|
} else {
|
||||||
@@ -119,6 +95,20 @@ export class Replayer {
|
|||||||
this.timerIds.forEach(clear);
|
this.timerIds.forEach(clear);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public resume(timeOffset = 0) {
|
||||||
|
for (const event of this.events) {
|
||||||
|
if (
|
||||||
|
event.timestamp < this.lastPlayedEvent.timestamp ||
|
||||||
|
event === this.lastPlayedEvent
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const delayToBaseline = this.getDelay(event);
|
||||||
|
const castFn = this.getCastFn(event);
|
||||||
|
this.later(castFn, delayToBaseline - timeOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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');
|
||||||
@@ -151,7 +141,7 @@ export class Replayer {
|
|||||||
event.data.source === IncrementalSource.MouseMove
|
event.data.source === IncrementalSource.MouseMove
|
||||||
) {
|
) {
|
||||||
const firstOffset = event.data.positions[0].timeOffset;
|
const firstOffset = event.data.positions[0].timeOffset;
|
||||||
// timeoffset is a negative offset to event.timestamp
|
// timeOffset is a negative offset to event.timestamp
|
||||||
const firstTimestamp = event.timestamp + firstOffset;
|
const firstTimestamp = event.timestamp + firstOffset;
|
||||||
event.data.positions = event.data.positions.map(p => {
|
event.data.positions = event.data.positions.map(p => {
|
||||||
return {
|
return {
|
||||||
@@ -159,12 +149,49 @@ export class Replayer {
|
|||||||
timeOffset: p.timeOffset - firstOffset,
|
timeOffset: p.timeOffset - firstOffset,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
return firstTimestamp - this.startTime;
|
return firstTimestamp - this.baselineTime;
|
||||||
}
|
}
|
||||||
return event.timestamp - this.startTime;
|
return event.timestamp - this.baselineTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
private rebuildFullSnapshot(event: fullSnapshotEvent) {
|
private getCastFn(event: eventWithTime, isSync = false) {
|
||||||
|
let castFn: undefined | (() => void);
|
||||||
|
switch (event.type) {
|
||||||
|
case EventType.DomContentLoaded:
|
||||||
|
case EventType.Load:
|
||||||
|
break;
|
||||||
|
case EventType.Meta:
|
||||||
|
castFn = () =>
|
||||||
|
this.emitter.emit('resize', {
|
||||||
|
width: event.data.width,
|
||||||
|
height: event.data.height,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case EventType.FullSnapshot:
|
||||||
|
castFn = () => {
|
||||||
|
this.rebuildFullSnapshot(event);
|
||||||
|
this.iframe.contentWindow!.scrollTo(event.data.initialOffset);
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case EventType.IncrementalSnapshot:
|
||||||
|
castFn = () => {
|
||||||
|
this.applyIncremental(event.data, isSync);
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
const wrappedCastFn = () => {
|
||||||
|
if (castFn) {
|
||||||
|
castFn();
|
||||||
|
}
|
||||||
|
this.lastPlayedEvent = event;
|
||||||
|
};
|
||||||
|
return wrappedCastFn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private rebuildFullSnapshot(
|
||||||
|
event: fullSnapshotEvent & { timestamp: number },
|
||||||
|
) {
|
||||||
mirror.map = rebuild(event.data.node, this.iframe.contentDocument!)[1];
|
mirror.map = rebuild(event.data.node, this.iframe.contentDocument!)[1];
|
||||||
// avoid form submit to refresh the iframe
|
// avoid form submit to refresh the iframe
|
||||||
this.iframe.contentDocument!.querySelectorAll('form').forEach(form => {
|
this.iframe.contentDocument!.querySelectorAll('form').forEach(form => {
|
||||||
|
|||||||
Reference in New Issue
Block a user