Reputation: 1959
I'm struggling with a drag and drop behavior with RXJS. I would like to start drag an element after 250ms mouse down for not hijack click events on that element.
So far the start drag works but stop drag never get called. Anyone know why?
let button = document.querySelector('.button');
let mouseDownStream = Rx.Observable.fromEvent(button, 'mousedown');
let mouseUpStream = Rx.Observable.fromEvent(button, 'mouseup');
let dragStream = mouseDownStream
.flatMap((event) => {
return Rx.Observable
.return(event)
.delay(250)
.takeUntil(mouseUpStream)
});
let dropStream = mouseUpStream
.flatMap((event) => {
return Rx.Observable
.return(event)
.skipUntil(dragStream)
});
dragStream.subscribe(event => console.log('start drag'));
dropStream.subscribe(event => console.log('stop drag'));
Upvotes: 1
Views: 2728
Reputation: 196
Universal drag event with TypeScript and newer rxjs
export const DragEvent = (selector:string):Observable<boolean> => {
//element where the drag event should be recorded (make sure element is in the dom)
const elem = document.querySelector(selector);
// touch events to handle mobile devices
const touchStart$ = fromEvent<TouchEvent>(elem, 'touchstart');
const touchEnd$ = fromEvent<TouchEvent>(elem, 'touchend');
const touchMove$ = fromEvent<TouchEvent>(elem, 'touchmove');
// mouse events for desktop users
const mouseDown$ = fromEvent<MouseEvent>(elem, 'mousedown');
const mouseUp$ = fromEvent<MouseEvent>(elem, 'mouseup');
const mouseMove$ = fromEvent<MouseEvent>(elem, 'mousemove');
//Mouse drag event
const mouseDragging$ = mouseMove$.pipe(
skipUntil(mouseDown$),
takeUntil(mouseUp$)
);
//
const mapToBoolean = (bool) => map(() => bool);
//universal drag event will emit true on drag (desktop/mobile) optional:add touchStart$ to the merge.
const move$ = merge(mouseDragging$, touchMove$).pipe(mapToBoolean(true);
//universal end of drag event will emit false on drag end (desktop/mobile)
const end$ = merge(mouseUp$, touchEnd$).pipe(mapToBoolean(false);
//merged to return true or false depending on user dragg
return merge(move$, end$).pipe(distinctUntilChanged());
}
Upvotes: 0
Reputation: 16892
I've updated your code-sample to make it run, what I did:
flatMap
s with switchMap
s (switchMap
is an alias for flatMapLatest
) this will ensure that it only takes the latest events and in case a new event is emitted, it will cancel any old subevent => in this case flatMap
might work okay, but it is safer to use switchMap
, also a rule of thumb: when in doubt: use switchMap
dropStream
is based on/initiated by dragStream
nowskipUntil
, which was a racing-condition issue because it would have first triggered after the next dragStream-emission after some mouseUp (which would require traveling back in time)button
to document
(more a convenience-thing, and not really essential for the while thing to work)let button = document.querySelector('.button');
let mouseDownStream = Rx.Observable.fromEvent(button, 'mousedown');
let mouseUpStream = Rx.Observable.fromEvent(document, 'mouseup');
let dragStream = mouseDownStream
.switchMap((event) => {
return Rx.Observable
.return(event)
.delay(250)
.takeUntil(mouseUpStream)
});
let dropStream = dragStream
.switchMap(() => mouseUpStream.take(1))
dragStream.subscribe(event => console.log('start drag'));
dropStream.subscribe(event => console.log('stop drag'));
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.all.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<button class="button">Button</button>
</body>
</html>
Upvotes: 5