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) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { INode } from 'rrweb-snapshot';
|
import { INode } from 'rrweb-snapshot';
|
||||||
import { mirror } from '../utils';
|
import { mirror, throttle } from '../utils';
|
||||||
import {
|
import {
|
||||||
mutationCallBack,
|
mutationCallBack,
|
||||||
textMutation,
|
textMutation,
|
||||||
@@ -7,6 +7,8 @@ import {
|
|||||||
removedNodeMutation,
|
removedNodeMutation,
|
||||||
addedNodeMutation,
|
addedNodeMutation,
|
||||||
observerParam,
|
observerParam,
|
||||||
|
mousemoveCallBack,
|
||||||
|
mousePosition,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
function initMutationObserver(cb: mutationCallBack): MutationObserver {
|
function initMutationObserver(cb: mutationCallBack): MutationObserver {
|
||||||
@@ -99,9 +101,49 @@ function initMutationObserver(cb: mutationCallBack): MutationObserver {
|
|||||||
return observer;
|
return observer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function initObservers(o: observerParam) {
|
function initMousemoveObserver(cb: mousemoveCallBack): () => void {
|
||||||
const mutationObserver = initMutationObserver(o.mutationCb);
|
let positions: mousePosition[] = [];
|
||||||
return {
|
let timeBaseline: number | null;
|
||||||
mutationObserver,
|
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 = {
|
export type incrementalSnapshotEvent = {
|
||||||
type: EventType.IncrementalSnapshot;
|
type: EventType.IncrementalSnapshot;
|
||||||
data: incrementalData;
|
data: incrementalData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export enum IncrementalSource {
|
||||||
|
Mutation,
|
||||||
|
MouseMove,
|
||||||
|
}
|
||||||
|
|
||||||
export type mutationData = {
|
export type mutationData = {
|
||||||
source: IncrementalSource.Mutation;
|
source: IncrementalSource.Mutation;
|
||||||
} & mutationCallbackParam;
|
} & mutationCallbackParam;
|
||||||
|
|
||||||
export type incrementalData = mutationData;
|
export type mousemoveData = {
|
||||||
|
source: IncrementalSource.MouseMove;
|
||||||
|
positions: mousePosition[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type incrementalData = mutationData | mousemoveData;
|
||||||
|
|
||||||
export type event =
|
export type event =
|
||||||
| domContentLoadedEvent
|
| domContentLoadedEvent
|
||||||
@@ -57,6 +63,7 @@ export type recordOptions = {
|
|||||||
|
|
||||||
export type observerParam = {
|
export type observerParam = {
|
||||||
mutationCb: mutationCallBack;
|
mutationCb: mutationCallBack;
|
||||||
|
mousemoveCb: mousemoveCallBack;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type textMutation = {
|
export type textMutation = {
|
||||||
@@ -92,8 +99,21 @@ type mutationCallbackParam = {
|
|||||||
|
|
||||||
export type mutationCallBack = (m: mutationCallbackParam) => void;
|
export type mutationCallBack = (m: mutationCallbackParam) => void;
|
||||||
|
|
||||||
|
export type mousemoveCallBack = (m: mousePosition[]) => void;
|
||||||
|
|
||||||
|
export type mousePosition = {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
timeOffset: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type Mirror = {
|
export type Mirror = {
|
||||||
map: idNodeMap;
|
map: idNodeMap;
|
||||||
getId: (n: INode) => number;
|
getId: (n: INode) => number;
|
||||||
getNode: (id: number) => INode;
|
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 = {
|
export const mirror: Mirror = {
|
||||||
map: {},
|
map: {},
|
||||||
@@ -9,3 +9,37 @@ export const mirror: Mirror = {
|
|||||||
return mirror.map[id];
|
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