add mouse movement observer
This commit is contained in:
@@ -60,6 +60,16 @@ function record(options: recordOptions) {
|
||||
},
|
||||
}),
|
||||
),
|
||||
mousemoveCb: positions =>
|
||||
emit(
|
||||
wrapEvent({
|
||||
type: EventType.IncrementalSnapshot,
|
||||
data: {
|
||||
source: IncrementalSource.MouseMove,
|
||||
positions,
|
||||
},
|
||||
}),
|
||||
),
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { INode } from 'rrweb-snapshot';
|
||||
import { mirror } from '../utils';
|
||||
import { mirror, throttle } from '../utils';
|
||||
import {
|
||||
mutationCallBack,
|
||||
textMutation,
|
||||
@@ -7,6 +7,8 @@ import {
|
||||
removedNodeMutation,
|
||||
addedNodeMutation,
|
||||
observerParam,
|
||||
mousemoveCallBack,
|
||||
mousePosition,
|
||||
} from '../types';
|
||||
|
||||
function initMutationObserver(cb: mutationCallBack): MutationObserver {
|
||||
@@ -99,9 +101,49 @@ function initMutationObserver(cb: mutationCallBack): MutationObserver {
|
||||
return observer;
|
||||
}
|
||||
|
||||
export default function initObservers(o: observerParam) {
|
||||
const mutationObserver = initMutationObserver(o.mutationCb);
|
||||
return {
|
||||
mutationObserver,
|
||||
function initMousemoveObserver(cb: mousemoveCallBack): () => void {
|
||||
let positions: mousePosition[] = [];
|
||||
let timeBaseline: number | null;
|
||||
const wrappedCb = throttle(() => {
|
||||
const totalOffset = Date.now() - timeBaseline!;
|
||||
cb(
|
||||
positions.map(p => {
|
||||
p.timeOffset -= totalOffset;
|
||||
return p;
|
||||
}),
|
||||
);
|
||||
positions = [];
|
||||
timeBaseline = null;
|
||||
}, 500);
|
||||
const updatePosition = throttle<MouseEvent>(
|
||||
evt => {
|
||||
const { clientX, clientY } = evt;
|
||||
if (!timeBaseline) {
|
||||
timeBaseline = Date.now();
|
||||
}
|
||||
positions.push({
|
||||
x: clientX,
|
||||
y: clientY,
|
||||
timeOffset: Date.now() - timeBaseline,
|
||||
});
|
||||
wrappedCb();
|
||||
},
|
||||
20,
|
||||
{
|
||||
trailing: false,
|
||||
},
|
||||
);
|
||||
document.addEventListener('mousemove', updatePosition);
|
||||
return () => {
|
||||
document.removeEventListener('mousemove', updatePosition);
|
||||
};
|
||||
}
|
||||
|
||||
export default function initObservers(o: observerParam) {
|
||||
const mutationObserver = initMutationObserver(o.mutationCb);
|
||||
const mousemoveHandler = initMousemoveObserver(o.mousemoveCb);
|
||||
return {
|
||||
mutationObserver,
|
||||
mousemoveHandler,
|
||||
};
|
||||
}
|
||||
|
||||
30
src/types.ts
30
src/types.ts
@@ -26,20 +26,26 @@ export type fullSnapshotEvent = {
|
||||
};
|
||||
};
|
||||
|
||||
export enum IncrementalSource {
|
||||
Mutation,
|
||||
}
|
||||
|
||||
export type incrementalSnapshotEvent = {
|
||||
type: EventType.IncrementalSnapshot;
|
||||
data: incrementalData;
|
||||
};
|
||||
|
||||
export enum IncrementalSource {
|
||||
Mutation,
|
||||
MouseMove,
|
||||
}
|
||||
|
||||
export type mutationData = {
|
||||
source: IncrementalSource.Mutation;
|
||||
} & mutationCallbackParam;
|
||||
|
||||
export type incrementalData = mutationData;
|
||||
export type mousemoveData = {
|
||||
source: IncrementalSource.MouseMove;
|
||||
positions: mousePosition[];
|
||||
};
|
||||
|
||||
export type incrementalData = mutationData | mousemoveData;
|
||||
|
||||
export type event =
|
||||
| domContentLoadedEvent
|
||||
@@ -57,6 +63,7 @@ export type recordOptions = {
|
||||
|
||||
export type observerParam = {
|
||||
mutationCb: mutationCallBack;
|
||||
mousemoveCb: mousemoveCallBack;
|
||||
};
|
||||
|
||||
export type textMutation = {
|
||||
@@ -92,8 +99,21 @@ type mutationCallbackParam = {
|
||||
|
||||
export type mutationCallBack = (m: mutationCallbackParam) => void;
|
||||
|
||||
export type mousemoveCallBack = (m: mousePosition[]) => void;
|
||||
|
||||
export type mousePosition = {
|
||||
x: number;
|
||||
y: number;
|
||||
timeOffset: number;
|
||||
};
|
||||
|
||||
export type Mirror = {
|
||||
map: idNodeMap;
|
||||
getId: (n: INode) => number;
|
||||
getNode: (id: number) => INode;
|
||||
};
|
||||
|
||||
export type throttleOptions = {
|
||||
leading?: boolean;
|
||||
trailing?: boolean;
|
||||
};
|
||||
|
||||
36
src/utils.ts
36
src/utils.ts
@@ -1,4 +1,4 @@
|
||||
import { Mirror } from './types';
|
||||
import { Mirror, throttleOptions } from './types';
|
||||
|
||||
export const mirror: Mirror = {
|
||||
map: {},
|
||||
@@ -9,3 +9,37 @@ export const mirror: Mirror = {
|
||||
return mirror.map[id];
|
||||
},
|
||||
};
|
||||
|
||||
// copy from underscore
|
||||
export function throttle<T>(
|
||||
func: (arg: T) => void,
|
||||
wait: number,
|
||||
options: throttleOptions = {},
|
||||
) {
|
||||
let timeout: number | null = null;
|
||||
let previous = 0;
|
||||
// tslint:disable-next-line: only-arrow-functions
|
||||
return function() {
|
||||
let now = Date.now();
|
||||
if (!previous && options.leading === false) {
|
||||
previous = now;
|
||||
}
|
||||
let remaining = wait - (now - previous);
|
||||
let context = this;
|
||||
let args = arguments;
|
||||
if (remaining <= 0 || remaining > wait) {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
}
|
||||
previous = now;
|
||||
func.apply(context, args);
|
||||
} else if (!timeout && options.trailing !== false) {
|
||||
timeout = setTimeout(() => {
|
||||
previous = options.leading === false ? 0 : Date.now();
|
||||
timeout = null;
|
||||
func.apply(context, args);
|
||||
}, remaining);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user