If so, how would you implement drag and drop?
RxJS mastery in 30 Days (07) : Observable Operators & Marble Diagrams]github.com/ShaofeiZi/3…
I suggest you go straight to the movie today
Today we’ll continue with the four operators of Take, First, takeUntil, and concatAll, and instantiate a simple drag-and-drop function.
Operators
take
Take is a very simple operator that terminates after taking the first few elements, as shown in the following example
var source = Rx.Observable.interval(1000);
var example = source.take(3);
example.subscribe({
next: (value) = > { console.log(value); },
error: (err) = > { console.log('Error: ' + err); },
complete: (a)= > { console.log('complete'); }});/ / 0
/ / 1
/ / 2
// completeCopy the code
JSBin | JSFiddle
Here you can see that our source originally emits an infinite number of elements, but here we use take(3) to fetch only the first three elements, and then complete.
The Marble diagram is shown below
source : ----0----- 1----2 ------ 3-.. take(3)
example: ----0----- 1----2 -|Copy the code
first
First fetches the first element sent by the Observable and terminates, behaving like take(1).
var source = Rx.Observable.interval(1000);
var example = source.first();
example.subscribe({
next: (value) = > { console.log(value); },
error: (err) = > { console.log('Error: ' + err); },
complete: (a)= > { console.log('complete'); }});/ / 0
// completeCopy the code
JSBin | JSFiddle
The Marble diagram is shown below
source : ----0----- 1----2 ------ 3-.. first() example: ----0|Copy the code
takeUntil
TakeUntil is commonly used in practice. It can make an Observable send a complete message when something happens, as shown in the following example
var source = Rx.Observable.interval(1000);
var click = Rx.Observable.fromEvent(document.body, 'click');
var example = source.takeUntil(click);
example.subscribe({
next: (value) = > { console.log(value); },
error: (err) = > { console.log('Error: ' + err); },
complete: (a)= > { console.log('complete'); }});/ / 0
/ / 1
/ / 2
/ / 3
// complete (Click on bodyCopy the code
JSBin | JSFiddle
Here we start by building an Observable with interval, which sends a value increasing from 0 every second. Then we pass in another Observable with takeUntil.
When the incoming Observable from takeUntil sends a value, the original Observable enters the complete state and sends a complete message. That is, this code will print a number every second (increasing from 0) until we click on the body and it will send a complete message.
If the Marble Diagram were drawn, it would look like this
source : ----0----- 1----2 ------- 3--
click : ----------------------c----
takeUntil(click)
example: ----0----- 1----2 -----|Copy the code
Observable completes when Click sends an element.
concatAll
Sometimes the element that our Observable sends is another Observable, like a two-dimensional array. The elements in the array are arrays. In this case, we can use concatAll to flatten it into a one-dimensional array. You can also directly think of concatAll as concating all elements together.
var click = Rx.Observable.fromEvent(document.body, 'click');
var source = click.map(e= > Rx.Observable.of(1.2.3));
var example = source.concatAll();
example.subscribe({
next: (value) = > { console.log(value); },
error: (err) = > { console.log('Error: ' + err); },
complete: (a)= > { console.log('complete'); }});Copy the code
JSBin | JSFiddle
In this example we immediately send 1,2, and 3 each time we click on the body, as represented by the Marble Diagram below
click : ------c------------c--------
map(e= > Rx.Observable.of(1.2.3))
source : ------o------------o--------
\ \
(123) | (123)|
concatAll()
example: ------(123) -- -- -- -- -- -- -- -- (123) -- -- -- -- -- -- -- -- -- -- -- --Copy the code
Here you can see that every time the source Observable sends a value, it is also an Observable. In this case, we can use concatAll to amortized the Source into example.
It should be noted that concatAll will process the first Observable emitted by source, and wait until the end of this observable before processing the next Observable emitted by source. Let’s use the following example to illustrate.
var obs1 = Rx.Observable.interval(1000).take(5);
var obs2 = Rx.Observable.interval(500).take(2);
var obs3 = Rx.Observable.interval(2000).take(1);
var source = Rx.Observable.of(obs1, obs2, obs3);
var example = source.concatAll();
example.subscribe({
next: (value) = > { console.log(value); },
error: (err) = > { console.log('Error: ' + err); },
complete: (a)= > { console.log('complete'); }});/ / 0
/ / 1
/ / 2
/ / 3
/ / 4
/ / 0
/ / 1
/ / 0
// completeCopy the code
JSBin | JSFiddle
Here you can see that the Source sends three Observables, but the behavior after concatAll is always to process the first Observable first, and then the next one after the current processing is complete.
The Marble diagram is shown below
source : (o1 o2 o3)|
\ \ \
-0-- 1-2 --- 3-4 -| 0- 1| ---0|
concatAll()
example: -0-- 1-2 --- 3-4 -0- 1---0|Copy the code
Simple drag and drop
Once we have learned the first few operators, we can easily do the drag-and-drop function. Let’s look at the requirements first
- First of all, I have a symbol on the screen (#drag)
- When the mouse presses mousedown on the #drag symbol, it starts to listen for the position of the mouse’s movement
- When the mouse left button is dropped (mouseup), it stops monitoring the mouse movement
- When mousemove is monitored, modify the style properties of the symbol
I have completed step 1, you can go directly to the following two links to do the exercises
- JSBin
- JSFiddle
The second step is to get each DOM event, symbol (#drag), and body.
const dragDOM = document.getElementById('drag');
const body = document.body;Copy the code
The reason to get the body is because both mousemove and mouseup should be listening throughout the body.
Step 3 We write out the listener events we use and use fromEvent to get each Observable.
- Listen mousedown on #drag
- Listen mouseup on the body
- Listen on the Body mousemove
const mouseDown = Rx.Observable.fromEvent(dragDOM, 'mousedown');
const mouseUp = Rx.Observable.fromEvent(body, 'mouseup');
const mouseMove = Rx.Observable.fromEvent(body, 'mousemove');Copy the code
Keep in mind that an Observable doesn’t listen until it subscribes. It must wait until it subscribes before it starts sending values.
Step 4 Start writing your logic
When mouseDown, it becomes a mouseMove event
const source = mouseDown.map(event= > mouseMove)Copy the code
MouseMove ends after mouseUp
Add takeUntil (mouseUp)
const source = mouseDown
.map(event= > mouseMove.takeUntil(mouseUp))Copy the code
So the source might look something like this
source: -------e--------------e-----
\ \
--m-m-m-m| -m--m-m--m-m|Copy the code
M stands for Mousemove Event
Flatten the source in one dimension with concatAll().
const source = mouseDown
.map(event= > mouseMove.takeUntil(mouseUp))
.concatAll();Copy the code
Convert mousemove Events into x and Y locations using map and subscribe.
source
.map(m= > {
return {
x: m.clientX,
y: m.clientY
}
})
.subscribe(pos= > {
dragDOM.style.left = pos.x + 'px';
dragDOM.style.top = pos.y + 'px';
})Copy the code
At this point we’re done with easy drag and drop functionality! The complete code is shown below
const dragDOM = document.getElementById('drag');
const body = document.body;
const mouseDown = Rx.Observable.fromEvent(dragDOM, 'mousedown');
const mouseUp = Rx.Observable.fromEvent(body, 'mouseup');
const mouseMove = Rx.Observable.fromEvent(body, 'mousemove');
mouseDown
.map(event= > mouseMove.takeUntil(mouseUp))
.concatAll()
.map(event= > ({ x: event.clientX, y: event.clientY }))
.subscribe(pos= > {
dragDOM.style.left = pos.x + 'px';
dragDOM.style.top = pos.y + 'px';
})Copy the code
I don’t know if the reader has noticed that the entire code is less than 15 lines, and our program is very readable as long as we can understand each operator.
This is just a simple drag-and-drop implementation, but it shows the power of RxJS to make our code simpler and easier to maintain!
The complete results are available here.
Today’s summary
Today we introduced four operators: Take, First, takeUntil, and concatAll, and completed a simple drag-and-drop feature that we will make more complete and integrate with other features!
I wonder if readers have learned anything today. If you have any questions, feel free to leave a comment below! If you like this article, please click the star +like next to the title for me, thank you.