Reputation: 21
I've been playing around a bit with RxJS 6. I am trying to implement dragstart, dragmove and dragend
. This is the code I have so far.
import { fromEvent } from 'rxjs';
import { concatMap } from 'rxjs/internal/operators/concatMap';
import { map } from 'rxjs/internal/operators/map';
import { takeUntil } from 'rxjs/internal/operators/takeUntil';
import { repeat } from 'rxjs/internal/operators/repeat';
import { first } from 'rxjs/internal/operators/first';
const mousedown = fromEvent<MouseEvent>(window, 'mousedown');
const mousemove = fromEvent<MouseEvent>(window, 'mousemove');
const mouseup = fromEvent<MouseEvent>(window, 'mouseup');
const dragstart = mousedown.pipe(
first()
);
const dragmove = mousedown.pipe(
concatMap((dragStartEvent) => mousemove.pipe(
takeUntil(mouseup))
)
);
const dragend = mousedown.pipe(
(dragEvent) => mouseup.pipe(first())
);
const log = (prefix: string) => (data: MouseEvent) => console.log(`${prefix}: x: ${data.clientX}, y:${data.clientY}`);
dragstart.subscribe(log('dragstart'));
dragmove.subscribe(log('dragmove'));
dragend.subscribe(log('dragend'));
The problem is that operators takeUntil
and first
mark the stream as completed.
This means that dragstart
and dragend
will only fire once. Is there a way to somehow reset the stream after this event happens?
For example by resetting the dragstart
stream when the mouseup
stream receives an event.
In the current implementation dragstart
and dragend
will be logged exactly once to the console after loading the page (& dragging). After that the stream is completed and no further events will be sent through it. I want to reset the stream every drag operation so the dragstart & dragend will work every drag operation rather than just the very first after loading the page.
First drag operation
Second drag operation
Upvotes: 2
Views: 526
Reputation: 1070
But you told to do so :)
If you remove the pipe and "first" operator from the dragstart stream
const dragstart = mousedown
the stream wont complete. The dragmove wont complete anyway because it is mapped from the mousedown, not the dragstart stream
If you tell to the resulting stream to pick only the first one, the stream will complete after emitting once.
This code should work:
const mousedown = fromEvent<MouseEvent>(window, 'mousedown');
const mousemove = fromEvent<MouseEvent>(window, 'mousemove');
const mouseup = fromEvent<MouseEvent>(window, 'mouseup');
// I dont think it's necessary to duplicate
const dragstart = mousedown
// No need to map to something else
const dragend = mouseup
const dragmove = dragstart.pipe(
concatMapTo(
mousemove.pipe(
takeUntil( dragend )
)
)
);
const log = (prefix: string) => (data: MouseEvent) => console.log(`${prefix}: x: ${data.clientX}, y:${data.clientY}`);
dragstart.subscribe(log('dragstart'));
dragmove.subscribe(log('dragmove'));
dragend.subscribe(log('dragend'));
Upvotes: 0
Reputation: 16498
Please see here for working example https://codepen.io/anon/pen/bMWjEVenter link description here
const { fromEvent } = Rx.Observable;
const target = document.querySelector('.box');
const mouseup = fromEvent(target, 'mouseup');
const mousemove = fromEvent(document, 'mousemove');
const mousedown = fromEvent(target, 'mousedown');
let log = (prefix: string, x:number, y:number) => console.log(`${prefix}: x: ${x}, y:${y}`);
const mousedrag = mousedown.selectMany((md) => {
const startX = md.clientX + window.scrollX,
startY = md.clientY + window.scrollY,
startLeft = parseInt(md.target.style.left, 10) || 0,
startTop = parseInt(md.target.style.top, 10) || 0;
return mousemove.map((mm) => {
mm.preventDefault();
log('mousemove',mm.clientX, mm.clientY);
return {
left: startLeft + mm.clientX - startX,
top: startTop + mm.clientY - startY
};
}).takeUntil(mouseup);
});
subscription = mousedrag.subscribe((pos) => {
log('dragstart',pos.top, pos.left)
target.style.top = pos.top + 'px';
target.style.left = pos.left + 'px';
});
.box {
position: relative;
width: 100px;
height: 100px;
background: red;
cursor: pointer;
border:solid 10px green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/2.5.2/rx.all.js"></script>
<div class="box"></div>
Upvotes: 0