# Mouse Drag with RxJS

Build mouse-drag behavior with RxJS by composing mousemove, mousedown, and mouseup streams using mergeMap and takeUntil.

Canonical URL: https://jdjuan.io/mouse-drag-rxjs
Author: Juan Herrera
Published: 2018-02-21

I found myself wanting to achieve something like this:

![Drawing circles by dragging the mouse with RxJS](/articles/mouse-drag-rxjs/demo.gif)

You can see the finished interaction above and follow the implementation step by step below. The original hosted demo and source project are no longer available.

## What I needed to do

1. 😊 Import RxJS
2. 😉 Get the events from the mouse
3. 😎 Draw a circle when the user drags the mouse

Simple right?

![Not really](/articles/mouse-drag-rxjs/not-really-1.gif)

_Not really._

## 1. Import RxJS

This is pretty straightforward. You just have to find a platform that allows you to add [RxJS](https://unpkg.com/@reactivex/rxjs@5.5.6/dist/global/Rx.js) right away like this:

![Adding RxJS in JS Bin](/articles/mouse-drag-rxjs/jsbin.gif)

_JS Bin_

And although JS Bin was perfect for this, I wanted to use Glitch, which had a lot of cool features as well. In this case you'd have to find RxJS in a UMD format and import it in your `index.html`:

```html
<script src="https://unpkg.com/@reactivex/rxjs@5.5.6/dist/global/Rx.js"></script>
```

![Importing the RxJS UMD bundle in Glitch](/articles/mouse-drag-rxjs/import-rxjs.gif)

_Look at line #8._

## 2. Get the events from the mouse

This is when RxJS starts to shine 🌟🌟🌟

You might already know this, but you can easily capture events from the DOM by using the `fromEvent()` Observable operator:

```js
const move$ = Rx.Observable.fromEvent(document, "mousemove");

move$.subscribe(console.log);
```

Which outputs the following information:

```text
MouseEvent { isTrusted: true, screenX: 638, screenY: 373, ... }
```

This was my face after:

![A surprised reaction](/articles/mouse-drag-rxjs/reaction.gif)

_I actually have better hair._

Once the `canvas.js` script is added it would look like this:

```js
move$.subscribe(drawCircle);
```

Which would draw a circle every time I move the mouse:

![Drawing a circle for every mousemove event](/articles/mouse-drag-rxjs/draw-circles.gif)

That there was a quantum leap! 😱 What's inside the canvas script? You might be wondering. Well, just Canvas drawing code. The original hosted source is no longer available, but the RxJS flow we are focusing on is all here.

Our app is drawing circles every time we move our mouse, but we need to draw circles only when the user is dragging the mouse (clicking and moving at the same time).

## 3. Draw a circle when the user drags the mouse

> “Huh! This should be simple” I said.

This is what I needed:

1. Create observables for two more types of events: `mousedown` and `mouseup` (so we could achieve the drag effect).
2. Make sure the emissions of our first observable (`move$`) started when the `mousedown` observable emitted.
3. Make sure the emissions of our first observable (`move$`) stopped when the `mouseup` observable emitted.

Simple right?

![Not really](/articles/mouse-drag-rxjs/not-really-2.gif)

_Not really._

My first approach to this problem was to use RxJS operators [`skipUntil`](https://www.learnrxjs.io/learn-rxjs/operators/filtering/skipuntil) and [`takeUntil`](https://www.learnrxjs.io/learn-rxjs/operators/filtering/takeuntil) to achieve the desired behavior.

We would skip every `move$` emission until the `down$` observable emitted. And then would take every `move$` emission until the `up$` observable emitted. This is how it looked:

```js
const move$ = Rx.Observable.fromEvent(document, "mousemove");
const down$ = Rx.Observable.fromEvent(document, "mousedown");
const up$ = Rx.Observable.fromEvent(document, "mouseup");

move$.skipUntil(down$).takeUntil(up$).subscribe(drawCircle);
```

And it worked! 🎉

…

Just once 😢😢😢

It turns out that when the `takeUntil()` operator is applied, the observable completes, which means that no more emissions will be received (after the user releases the click button).

Uhmmm 🤔🤔🤔

Unless you use the [`repeat()`](https://rxmarbles.com/#repeat) operator to keep the subscription alive! 🎉🎉🎉

```js
move$.skipUntil(down$).takeUntil(up$).repeat().subscribe(drawCircle);
```

And it worked! 🎉

…

In a very awful way 😢😢😢

While in the first place it allows me to keep the Observable alive, it repeats the events indefinitely which looks like an overkill.

To quote Matthew Podwysocki:

> “RxJS has so many ways to get the same answer”

Here is when [Matt](https://twitter.com/mattpodwysocki) and [Cédric](https://twitter.com/CedricSoulas) gave me a hand. They helped me spot a different way to achieve it using the [`mergeMap()`](https://www.learnrxjs.io/learn-rxjs/operators/transformation/mergemap) operator.

![A confused first reaction to mergeMap](/articles/mouse-drag-rxjs/merge-map.jpeg)

_My first glance at it._

I didn't really understood how it worked until I watched [this video](https://www.youtube.com/watch?v=b59tcUwfpWU&t=402s) and played with its [jsfiddle](https://jsfiddle.net/j8fjm1se/63/): If you type in the first box nothing will be emitted until you type in the second box 👇👇👇

We don't want to draw anything until the user has clicked. So we can use the `mergeMap` operator here (which achieves the same result as `skipUntil` above):

```js
down$.mergeMap(() => move$).subscribe(drawCircle);
```

**BUT** this time the inner observable (a.k.a. `move$`) will re-subscribe every time the outer observable (a.k.a. `down$`) emits.

![Exactly](/articles/mouse-drag-rxjs/exactly.gif)

_Exactly!_

That means we can use the `takeUntil` operator safely. Because the overall observable will remain alive! 💚

```js
down$.mergeMap(() => move$.takeUntil(up$)).subscribe(drawCircle);
```

_Line #10._

Yes, that's it. To achieve mouse dragging you just need `mergeMap` and `takeUntil` 😉

I hope this post was helpful to you. If you have any question or feedback, put it in the comments. I reply to all of them! 👇👇👇

Thanks for reading. The complete implementation is included in the examples above.

Thanks to [Matt](https://twitter.com/mattpodwysocki) and [Cédric](https://twitter.com/CedricSoulas) again, they were super collaborative and helpful!
