简体   繁体   中英

RxJs mousemove event with condition

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/rxjs@6.3.2/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 (ie 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?

So if i understand your question correctly you want to:

  • Track all mouse movements ( mousemove event stream - fromevent )
  • After movement stops for a certain time ( debounce )
  • Verify it is within a bounding box ( 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 => {});

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/rxjs@6.3.2/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!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM