The desktop pointer cursor is not representative of mobile (#662)
* Simplify css for click animation
* Refactor to transfer responsibility for casting multiple synchronous to index.ts from machine.ts (so they can be dealt with in bulk)
* During synchronous application of a batch of events, move the mouse to the last position so that it's in the correct place when the timer starts
- previous `needCastInSyncMode` added in 4bf533a675 meant that the isSync versions of MouseMove/TouchMove were being accidentally ignored
- each synchronous MouseMove would have resulted in a separate mouse position update
- the Click/TouchStart/TouchEnd events didn't have an async version
* The desktop pointer cursor is not representative of what is happening on a mobile device.
Instead, check a recording for any presence of a Touch event, and switch to a touch visualisation mode for the entire recording.
(for now, we use this mode even for mixed touch/mouse devices - this could be improved upon in future)
Show a round circle representing the users' finger which is visible only between TouchStart and TouchEnd events
Again this can be evolved upon, but this change should be a good start in the right direction.
* It's more correct to not have a transition for repositioning of touch as user can lift finger off screen and place elsewhere; however we can now have much smoother touch movement during the .touch-active phase as we know the finger is on the screen. This has a .25s delaying effect on the touch position which IMO is acceptable; e.g. scroll position can lag behind a touch movement and this seems to bring them more in sync
* Ensure we end up with the correct touch-active state after a series of synchronous events
* Important to discontinue tail animations and position transitions when user has lifted their finger and placed it into a new position. This is apparent in a replay session where the user is scrolling the page using repeated TouchMove bottom-to-top movements
* Simplify by unwrapping `mouseState.touchActive` and `mouseState.pos` into their own global vars
This commit is contained in:
@@ -37,6 +37,7 @@ import {
|
|||||||
ElementState,
|
ElementState,
|
||||||
styleAttributeValue,
|
styleAttributeValue,
|
||||||
styleValueWithPriority,
|
styleValueWithPriority,
|
||||||
|
mouseMovePos,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import {
|
import {
|
||||||
createMirror,
|
createMirror,
|
||||||
@@ -77,6 +78,13 @@ const defaultMouseTailConfig = {
|
|||||||
strokeStyle: 'red',
|
strokeStyle: 'red',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
function indicatesTouchDevice(e: eventWithTime) {
|
||||||
|
return e.type == EventType.IncrementalSnapshot &&
|
||||||
|
(e.data.source == IncrementalSource.TouchMove ||
|
||||||
|
(e.data.source == IncrementalSource.MouseInteraction &&
|
||||||
|
e.data.type == MouseInteractions.TouchStart));
|
||||||
|
}
|
||||||
|
|
||||||
export class Replayer {
|
export class Replayer {
|
||||||
public wrapper: HTMLDivElement;
|
public wrapper: HTMLDivElement;
|
||||||
public iframe: HTMLIFrameElement;
|
public iframe: HTMLIFrameElement;
|
||||||
@@ -117,6 +125,9 @@ export class Replayer {
|
|||||||
|
|
||||||
private newDocumentQueue: addedNodeMutation[] = [];
|
private newDocumentQueue: addedNodeMutation[] = [];
|
||||||
|
|
||||||
|
private mousePos: mouseMovePos | null = null;
|
||||||
|
private touchActive: boolean | null = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
events: Array<eventWithTime | string>,
|
events: Array<eventWithTime | string>,
|
||||||
config?: Partial<playerConfig>,
|
config?: Partial<playerConfig>,
|
||||||
@@ -144,6 +155,7 @@ export class Replayer {
|
|||||||
|
|
||||||
this.handleResize = this.handleResize.bind(this);
|
this.handleResize = this.handleResize.bind(this);
|
||||||
this.getCastFn = this.getCastFn.bind(this);
|
this.getCastFn = this.getCastFn.bind(this);
|
||||||
|
this.applyEventsSynchronously = this.applyEventsSynchronously.bind(this);
|
||||||
this.emitter.on(ReplayerEvents.Resize, this.handleResize as Handler);
|
this.emitter.on(ReplayerEvents.Resize, this.handleResize as Handler);
|
||||||
|
|
||||||
this.setupDom();
|
this.setupDom();
|
||||||
@@ -197,6 +209,7 @@ export class Replayer {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
getCastFn: this.getCastFn,
|
getCastFn: this.getCastFn,
|
||||||
|
applyEventsSynchronously: this.applyEventsSynchronously,
|
||||||
emitter: this.emitter,
|
emitter: this.emitter,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -250,6 +263,10 @@ export class Replayer {
|
|||||||
);
|
);
|
||||||
}, 1);
|
}, 1);
|
||||||
}
|
}
|
||||||
|
if (this.service.state.context.events.find(indicatesTouchDevice)) {
|
||||||
|
this.mouse.classList.add('touch-device');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public on(event: string, handler: Handler) {
|
public on(event: string, handler: Handler) {
|
||||||
@@ -373,6 +390,9 @@ export class Replayer {
|
|||||||
const event = this.config.unpackFn
|
const event = this.config.unpackFn
|
||||||
? this.config.unpackFn(rawEvent as string)
|
? this.config.unpackFn(rawEvent as string)
|
||||||
: (rawEvent as eventWithTime);
|
: (rawEvent as eventWithTime);
|
||||||
|
if (indicatesTouchDevice(event)) {
|
||||||
|
this.mouse.classList.add('touch-device');
|
||||||
|
}
|
||||||
Promise.resolve().then(() =>
|
Promise.resolve().then(() =>
|
||||||
this.service.send({ type: 'ADD_EVENT', payload: { event } }),
|
this.service.send({ type: 'ADD_EVENT', payload: { event } }),
|
||||||
);
|
);
|
||||||
@@ -443,6 +463,43 @@ export class Replayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private applyEventsSynchronously(events: Array<eventWithTime>) {
|
||||||
|
for (const event of events) {
|
||||||
|
switch (event.type) {
|
||||||
|
case EventType.DomContentLoaded:
|
||||||
|
case EventType.Load:
|
||||||
|
case EventType.Custom:
|
||||||
|
continue;
|
||||||
|
case EventType.FullSnapshot:
|
||||||
|
case EventType.Meta:
|
||||||
|
case EventType.Plugin:
|
||||||
|
break;
|
||||||
|
case EventType.IncrementalSnapshot:
|
||||||
|
switch (event.data.source) {
|
||||||
|
case IncrementalSource.MediaInteraction:
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const castFn = this.getCastFn(event, true);
|
||||||
|
castFn();
|
||||||
|
}
|
||||||
|
if (this.mousePos) {
|
||||||
|
this.moveAndHover(this.mousePos.x, this.mousePos.y, this.mousePos.id, true, this.mousePos.debugData);
|
||||||
|
}
|
||||||
|
this.mousePos = null;
|
||||||
|
if (this.touchActive === true) {
|
||||||
|
this.mouse.classList.add('touch-active');
|
||||||
|
} else if (this.touchActive === false) {
|
||||||
|
this.mouse.classList.remove('touch-active');
|
||||||
|
}
|
||||||
|
this.touchActive = null;
|
||||||
|
}
|
||||||
|
|
||||||
private getCastFn(event: eventWithTime, isSync = false) {
|
private getCastFn(event: eventWithTime, isSync = false) {
|
||||||
let castFn: undefined | (() => void);
|
let castFn: undefined | (() => void);
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
@@ -803,12 +860,17 @@ export class Replayer {
|
|||||||
case IncrementalSource.MouseMove:
|
case IncrementalSource.MouseMove:
|
||||||
if (isSync) {
|
if (isSync) {
|
||||||
const lastPosition = d.positions[d.positions.length - 1];
|
const lastPosition = d.positions[d.positions.length - 1];
|
||||||
this.moveAndHover(d, lastPosition.x, lastPosition.y, lastPosition.id);
|
this.mousePos = {
|
||||||
|
x: lastPosition.x,
|
||||||
|
y: lastPosition.y,
|
||||||
|
id: lastPosition.id,
|
||||||
|
debugData: d,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
d.positions.forEach((p) => {
|
d.positions.forEach((p) => {
|
||||||
const action = {
|
const action = {
|
||||||
doAction: () => {
|
doAction: () => {
|
||||||
this.moveAndHover(d, p.x, p.y, p.id);
|
this.moveAndHover(p.x, p.y, p.id, isSync, d);
|
||||||
},
|
},
|
||||||
delay:
|
delay:
|
||||||
p.timeOffset +
|
p.timeOffset +
|
||||||
@@ -857,19 +919,51 @@ export class Replayer {
|
|||||||
case MouseInteractions.Click:
|
case MouseInteractions.Click:
|
||||||
case MouseInteractions.TouchStart:
|
case MouseInteractions.TouchStart:
|
||||||
case MouseInteractions.TouchEnd:
|
case MouseInteractions.TouchEnd:
|
||||||
/**
|
if (isSync) {
|
||||||
* Click has no visual impact when replaying and may
|
let touchActive: boolean | undefined;
|
||||||
* trigger navigation when apply to an <a> link.
|
if (d.type === MouseInteractions.TouchStart) {
|
||||||
* So we will not call click(), instead we add an
|
this.touchActive = true;
|
||||||
* animation to the mouse element which indicate user
|
} else if (d.type === MouseInteractions.TouchEnd) {
|
||||||
* clicked at this moment.
|
this.touchActive = false;
|
||||||
*/
|
}
|
||||||
if (!isSync) {
|
this.mousePos = {
|
||||||
this.moveAndHover(d, d.x, d.y, d.id);
|
x: d.x,
|
||||||
this.mouse.classList.remove('active');
|
y: d.y,
|
||||||
// tslint:disable-next-line
|
id: d.id,
|
||||||
void this.mouse.offsetWidth;
|
debugData: d,
|
||||||
this.mouse.classList.add('active');
|
};
|
||||||
|
} else {
|
||||||
|
if (d.type === MouseInteractions.TouchStart) {
|
||||||
|
// don't draw a trail as user has lifted finger and is placing at a new point
|
||||||
|
this.tailPositions.length = 0;
|
||||||
|
}
|
||||||
|
this.moveAndHover(d.x, d.y, d.id, isSync, d);
|
||||||
|
if (d.type === MouseInteractions.Click) {
|
||||||
|
/*
|
||||||
|
* don't want target.click() here as could trigger an iframe navigation
|
||||||
|
* instead any effects of the click should already be covered by mutations
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* removal and addition of .active class (along with void line to trigger repaint)
|
||||||
|
* triggers the 'click' css animation in styles/style.css
|
||||||
|
*/
|
||||||
|
this.mouse.classList.remove('active');
|
||||||
|
// tslint:disable-next-line
|
||||||
|
void this.mouse.offsetWidth;
|
||||||
|
this.mouse.classList.add('active');
|
||||||
|
} else if (d.type === MouseInteractions.TouchStart) {
|
||||||
|
void this.mouse.offsetWidth; // needed for the position update of moveAndHover to apply without the .touch-active transition
|
||||||
|
this.mouse.classList.add('touch-active');
|
||||||
|
} else if (d.type === MouseInteractions.TouchEnd) {
|
||||||
|
this.mouse.classList.remove('touch-active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MouseInteractions.TouchCancel:
|
||||||
|
if (isSync) {
|
||||||
|
this.touchActive = false;
|
||||||
|
} else {
|
||||||
|
this.mouse.classList.remove('touch-active');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -1487,10 +1581,10 @@ export class Replayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private moveAndHover(d: incrementalData, x: number, y: number, id: number) {
|
private moveAndHover(x: number, y: number, id: number, isSync: boolean, debugData: incrementalData) {
|
||||||
const target = this.mirror.getNode(id);
|
const target = this.mirror.getNode(id);
|
||||||
if (!target) {
|
if (!target) {
|
||||||
return this.debugNodeNotFound(d, id);
|
return this.debugNodeNotFound(debugData, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
const base = getBaseDimension(target, this.iframe);
|
const base = getBaseDimension(target, this.iframe);
|
||||||
@@ -1499,7 +1593,9 @@ export class Replayer {
|
|||||||
|
|
||||||
this.mouse.style.left = `${_x}px`;
|
this.mouse.style.left = `${_x}px`;
|
||||||
this.mouse.style.top = `${_y}px`;
|
this.mouse.style.top = `${_y}px`;
|
||||||
this.drawMouseTail({ x: _x, y: _y });
|
if (!isSync) {
|
||||||
|
this.drawMouseTail({ x: _x, y: _y });
|
||||||
|
}
|
||||||
this.hoverElements((target as Node) as Element);
|
this.hoverElements((target as Node) as Element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
IncrementalSource,
|
IncrementalSource,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import { Timer, addDelay } from './timer';
|
import { Timer, addDelay } from './timer';
|
||||||
import { needCastInSyncMode } from '../utils';
|
|
||||||
|
|
||||||
export type PlayerContext = {
|
export type PlayerContext = {
|
||||||
events: eventWithTime[];
|
events: eventWithTime[];
|
||||||
@@ -77,11 +76,12 @@ export function discardPriorSnapshots(
|
|||||||
|
|
||||||
type PlayerAssets = {
|
type PlayerAssets = {
|
||||||
emitter: Emitter;
|
emitter: Emitter;
|
||||||
|
applyEventsSynchronously(events: Array<eventWithTime>): void;
|
||||||
getCastFn(event: eventWithTime, isSync: boolean): () => void;
|
getCastFn(event: eventWithTime, isSync: boolean): () => void;
|
||||||
};
|
};
|
||||||
export function createPlayerService(
|
export function createPlayerService(
|
||||||
context: PlayerContext,
|
context: PlayerContext,
|
||||||
{ getCastFn, emitter }: PlayerAssets,
|
{ getCastFn, applyEventsSynchronously, emitter }: PlayerAssets,
|
||||||
) {
|
) {
|
||||||
const playerMachine = createMachine<PlayerContext, PlayerEvent, PlayerState>(
|
const playerMachine = createMachine<PlayerContext, PlayerEvent, PlayerState>(
|
||||||
{
|
{
|
||||||
@@ -186,6 +186,7 @@ export function createPlayerService(
|
|||||||
emitter.emit(ReplayerEvents.PlayBack);
|
emitter.emit(ReplayerEvents.PlayBack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const syncEvents = new Array<eventWithTime>();
|
||||||
const actions = new Array<actionWithDelay>();
|
const actions = new Array<actionWithDelay>();
|
||||||
for (const event of neededEvents) {
|
for (const event of neededEvents) {
|
||||||
if (
|
if (
|
||||||
@@ -196,14 +197,10 @@ export function createPlayerService(
|
|||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const isSync = event.timestamp < baselineTime;
|
if (event.timestamp < baselineTime) {
|
||||||
if (isSync && !needCastInSyncMode(event)) {
|
syncEvents.push(event);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const castFn = getCastFn(event, isSync);
|
|
||||||
if (isSync) {
|
|
||||||
castFn();
|
|
||||||
} else {
|
} else {
|
||||||
|
const castFn = getCastFn(event, false);
|
||||||
actions.push({
|
actions.push({
|
||||||
doAction: () => {
|
doAction: () => {
|
||||||
castFn();
|
castFn();
|
||||||
@@ -213,6 +210,7 @@ export function createPlayerService(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
applyEventsSynchronously(syncEvents);
|
||||||
emitter.emit(ReplayerEvents.Flush);
|
emitter.emit(ReplayerEvents.Flush);
|
||||||
timer.addActions(actions);
|
timer.addActions(actions);
|
||||||
timer.start();
|
timer.start();
|
||||||
|
|||||||
@@ -5,25 +5,48 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
transition: 0.05s linear;
|
transition: left 0.05s linear, top 0.05s linear;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
background-position: center center;
|
background-position: center center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-image: url('data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9JzMwMHB4JyB3aWR0aD0nMzAwcHgnICBmaWxsPSIjMDAwMDAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGRhdGEtbmFtZT0iTGF5ZXIgMSIgdmlld0JveD0iMCAwIDUwIDUwIiB4PSIwcHgiIHk9IjBweCI+PHRpdGxlPkRlc2lnbl90bnA8L3RpdGxlPjxwYXRoIGQ9Ik00OC43MSw0Mi45MUwzNC4wOCwyOC4yOSw0NC4zMywxOEExLDEsMCwwLDAsNDQsMTYuMzlMMi4zNSwxLjA2QTEsMSwwLDAsMCwxLjA2LDIuMzVMMTYuMzksNDRhMSwxLDAsMCwwLDEuNjUuMzZMMjguMjksMzQuMDgsNDIuOTEsNDguNzFhMSwxLDAsMCwwLDEuNDEsMGw0LjM4LTQuMzhBMSwxLDAsMCwwLDQ4LjcxLDQyLjkxWm0tNS4wOSwzLjY3TDI5LDMyYTEsMSwwLDAsMC0xLjQxLDBsLTkuODUsOS44NUwzLjY5LDMuNjlsMzguMTIsMTRMMzIsMjcuNThBMSwxLDAsMCwwLDMyLDI5TDQ2LjU5LDQzLjYyWiI+PC9wYXRoPjwvc3ZnPg==');
|
background-image: url('data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9JzMwMHB4JyB3aWR0aD0nMzAwcHgnICBmaWxsPSIjMDAwMDAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGRhdGEtbmFtZT0iTGF5ZXIgMSIgdmlld0JveD0iMCAwIDUwIDUwIiB4PSIwcHgiIHk9IjBweCI+PHRpdGxlPkRlc2lnbl90bnA8L3RpdGxlPjxwYXRoIGQ9Ik00OC43MSw0Mi45MUwzNC4wOCwyOC4yOSw0NC4zMywxOEExLDEsMCwwLDAsNDQsMTYuMzlMMi4zNSwxLjA2QTEsMSwwLDAsMCwxLjA2LDIuMzVMMTYuMzksNDRhMSwxLDAsMCwwLDEuNjUuMzZMMjguMjksMzQuMDgsNDIuOTEsNDguNzFhMSwxLDAsMCwwLDEuNDEsMGw0LjM4LTQuMzhBMSwxLDAsMCwwLDQ4LjcxLDQyLjkxWm0tNS4wOSwzLjY3TDI5LDMyYTEsMSwwLDAsMC0xLjQxLDBsLTkuODUsOS44NUwzLjY5LDMuNjlsMzguMTIsMTRMMzIsMjcuNThBMSwxLDAsMCwwLDMyLDI5TDQ2LjU5LDQzLjYyWiI+PC9wYXRoPjwvc3ZnPg==');
|
||||||
|
border-color: transparent; /* otherwise we transition from black when .touch-device class is added */
|
||||||
}
|
}
|
||||||
.replayer-mouse::after {
|
.replayer-mouse::after {
|
||||||
content: '';
|
content: '';
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
border-radius: 10px;
|
|
||||||
background: rgb(73, 80, 246);
|
background: rgb(73, 80, 246);
|
||||||
transform: translate(-10px, -10px);
|
border-radius: 100%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
opacity: 0.3;
|
opacity: 0.3;
|
||||||
}
|
}
|
||||||
.replayer-mouse.active::after {
|
.replayer-mouse.active::after {
|
||||||
animation: click 0.2s ease-in-out 1;
|
animation: click 0.2s ease-in-out 1;
|
||||||
}
|
}
|
||||||
|
.replayer-mouse.touch-device {
|
||||||
|
background-image: none; /* there's no passive cursor on touch-only screens */
|
||||||
|
width: 70px;
|
||||||
|
height: 70px;
|
||||||
|
border-width: 4px;
|
||||||
|
border-style: solid;
|
||||||
|
border-radius: 100%;
|
||||||
|
margin-left: -37px;
|
||||||
|
margin-top: -37px;
|
||||||
|
border-color: rgba(73, 80, 246, 0);
|
||||||
|
transition: left 0s linear, top 0s linear, border-color 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
.replayer-mouse.touch-device.touch-active {
|
||||||
|
border-color: rgba(73, 80, 246, 1);
|
||||||
|
transition: left 0.25s linear, top 0.25s linear, border-color 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
.replayer-mouse.touch-device::after {
|
||||||
|
opacity: 0; /* there's no passive cursor on touch-only screens */
|
||||||
|
}
|
||||||
|
.replayer-mouse.touch-device.active::after {
|
||||||
|
animation: touch-click 0.2s ease-in-out 1;
|
||||||
|
}
|
||||||
.replayer-mouse-tail {
|
.replayer-mouse-tail {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
@@ -34,14 +57,23 @@
|
|||||||
opacity: 0.3;
|
opacity: 0.3;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
border-radius: 10px;
|
|
||||||
transform: translate(-10px, -10px);
|
|
||||||
}
|
}
|
||||||
50% {
|
50% {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
border-radius: 5px;
|
}
|
||||||
transform: translate(-5px, -5px);
|
}
|
||||||
|
|
||||||
|
@keyframes touch-click {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.5;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -352,6 +352,13 @@ export type mousePosition = {
|
|||||||
timeOffset: number;
|
timeOffset: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type mouseMovePos = {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
id: number;
|
||||||
|
debugData: incrementalData;
|
||||||
|
};
|
||||||
|
|
||||||
export enum MouseInteractions {
|
export enum MouseInteractions {
|
||||||
MouseUp,
|
MouseUp,
|
||||||
MouseDown,
|
MouseDown,
|
||||||
@@ -363,6 +370,7 @@ export enum MouseInteractions {
|
|||||||
TouchStart,
|
TouchStart,
|
||||||
TouchMove_Departed, // we will start a separate observer for touch move event
|
TouchMove_Departed, // we will start a separate observer for touch move event
|
||||||
TouchEnd,
|
TouchEnd,
|
||||||
|
TouchCancel,
|
||||||
}
|
}
|
||||||
|
|
||||||
type mouseInteractionParam = {
|
type mouseInteractionParam = {
|
||||||
|
|||||||
@@ -314,38 +314,6 @@ export function polyfill(win = window) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function needCastInSyncMode(event: eventWithTime): boolean {
|
|
||||||
switch (event.type) {
|
|
||||||
case EventType.DomContentLoaded:
|
|
||||||
case EventType.Load:
|
|
||||||
case EventType.Custom:
|
|
||||||
return false;
|
|
||||||
case EventType.FullSnapshot:
|
|
||||||
case EventType.Meta:
|
|
||||||
case EventType.Plugin:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (event.data.source) {
|
|
||||||
case IncrementalSource.MouseMove:
|
|
||||||
case IncrementalSource.MouseInteraction:
|
|
||||||
case IncrementalSource.TouchMove:
|
|
||||||
case IncrementalSource.MediaInteraction:
|
|
||||||
return false;
|
|
||||||
case IncrementalSource.ViewportResize:
|
|
||||||
case IncrementalSource.StyleSheetRule:
|
|
||||||
case IncrementalSource.Scroll:
|
|
||||||
case IncrementalSource.Input:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TreeNode = {
|
export type TreeNode = {
|
||||||
id: number;
|
id: number;
|
||||||
mutation: addedNodeMutation;
|
mutation: addedNodeMutation;
|
||||||
|
|||||||
1
packages/rrweb/typings/utils.d.ts
vendored
1
packages/rrweb/typings/utils.d.ts
vendored
@@ -15,7 +15,6 @@ export declare function isIgnored(n: Node | INode): boolean;
|
|||||||
export declare function isAncestorRemoved(target: INode, mirror: Mirror): boolean;
|
export declare function isAncestorRemoved(target: INode, mirror: Mirror): boolean;
|
||||||
export declare function isTouchEvent(event: MouseEvent | TouchEvent): event is TouchEvent;
|
export declare function isTouchEvent(event: MouseEvent | TouchEvent): event is TouchEvent;
|
||||||
export declare function polyfill(win?: Window & typeof globalThis): void;
|
export declare function polyfill(win?: Window & typeof globalThis): void;
|
||||||
export declare function needCastInSyncMode(event: eventWithTime): boolean;
|
|
||||||
export declare type TreeNode = {
|
export declare type TreeNode = {
|
||||||
id: number;
|
id: number;
|
||||||
mutation: addedNodeMutation;
|
mutation: addedNodeMutation;
|
||||||
|
|||||||
Reference in New Issue
Block a user