Reputation: 6742
I'm struggling with this and I have no idea how to proceed. I want to capture mouse event only when the user stops with mousemove
for certain time and it's inside specific element.
const { fromEvent } = rxjs;
const { debounceTime, tap, filter } = rxjs.operators;
const square = document.querySelectorAll("#square");
let isCursorOverSquare = true;
const move$ = fromEvent(square, "mousemove");
const enter$ = fromEvent(square, "mouseenter");
const leave$ = fromEvent(square, "mouseleave");
enter$.pipe(
tap(() => isCursorOverSquare = true)
).subscribe();
leave$.pipe(
tap(() => isCursorOverSquare = false)
).subscribe();
move$
.pipe(
debounceTime(2000),
filter(() => isCursorOverSquare)
)
.subscribe(
(e) => {
console.log(e.target);
});
#square {
width: 200px;
height: 200px;
display: block;
border: 1px solid black;
}
<script src="https://unpkg.com/[email protected]/bundles/rxjs.umd.min.js"></script>
<div id="square"></div>
The thing I can't figure out is, how to skip the console.log
, if the user moves from square to outside (i.e. handle the logic only, when user is with cursor inside the square).
EDIT:
I managed to work it, but it's not the "RxJs" way. Now I'm setting the isCursorOverSquare
variable to true
and false
and then I use filter
operator. Is there a "nicer" more reactive way, to handle this?
Upvotes: 0
Views: 2975
Reputation: 1962
The issue that you have is the last mousemove
event is triggered when the cursor is still in the square, but the debounce delays the observable until after the cursor has left the square. You can solve this issue by only taking the observable until the mouse has left the square. Here is the complete code for this answer:
<head>
<style>
#square {
width: 200px;
height: 200px;
display: block;
border: 1px solid black;
}
</style>
</head>
<body>
<div id="square"></div>
<script src="https://unpkg.com/[email protected]/bundles/rxjs.umd.min.js"></script>
<script>
const { fromEvent } = rxjs;
const { debounceTime, repeat , takeUntil } = rxjs.operators;
const square = document.getElementById("square");
const move$ = fromEvent(square, "mousemove").pipe(debounceTime(2000));
const enter$ = fromEvent(square, "mouseenter");
const leave$ = fromEvent(square, "mouseleave");
move$.pipe(takeUntil(leave$), repeat()).subscribe((e) => console.log(e.target));
</script>
</body>
The repeat
operator is necessary as otherwise once the mouse has left the square the first time, the observable will not be repeated when the mouse next enters the square. If your intended behaviour is for the observable to stop emitting after the mouse has left the square for the first time, feel free to remove the repeat operator. Hope this helps you, let me know if you have any questions!
Upvotes: 0
Reputation: 9425
So if i understand your question correctly you want to:
mousemove
event stream - fromevent
)debounce
)filter
)So depending on performance you can either always event the mousemoves or only start mousemove eventing after entering the square using the .switchMap()
operator:
enter$
.switchMap(_ => $moves
.debounceTime(2000)
.takeUntil(leave$)
)
.subscribe(finalMouseMoveEventInSquare => {});
Upvotes: 2