javascript - RxJS: Locked horizontal or vertical mouse dragging -
i want build interface can drag in various directions mode selected after distance. instance if drag 25px horizontally locks mode , stays there until release mouse. if drag vertically same. other actions happen if click or press , hold long time.
here's simplified fiddle illustrating goal: https://jsfiddle.net/ud37p0y2/2/
it seems reactive programming perfect can't seem figure out how start these modes , stick them until release mouse. starting point has been many drag , drop examples can't seem take further..
some code (typescript):
var mousedown = rx.observable.fromevent($element[0], 'mousedown').select((event: mouseevent): ipoint => { event.preventdefault(); return { x: event.clientx, y: event.clienty }; }); var mouseup = rx.observable.fromevent($element[0], 'mouseup'); var mousemove = rx.observable.fromevent($element[0], 'mousemove'); var mousedrag = mousedown.selectmany((mousedownpos: ipoint) => { return mousemove.select((event: mouseevent) => { return { x: event.clientx - mousedownpos.x, y: event.clienty - mousedownpos.y }; }).takeuntil(mouseup); }); var horizontaldrag = mousedrag.filter((pos: ipoint) => { return pos.x < -25 || pos.x > 25; }); // how continue here? horizontaldrag.subscribe((pos: ipoint) => { console.log('drag'); // fires time, i'd once when mode starts , else called every time mouse has moved });
from here i'd observable horizontal drag, vertical drag, , hold events. after mode has started other ones should disabled instance drag wouldn't trigger long press events.
i use combination of amb
+ skipwhile
.
amb
give behavior of locking in state,skipwhile
prevent event firing until pass threshold period of time.
the core logic this:
//waits either x or y emit propagates 1 return rx.observable.amb( mousemove .pluck('clientx') //wait until threshold reached .skipwhile(function (x) { return math.abs(startat.clientx - x) < 25; }) //transform outgoing event .map(function (x) { return { prop: 'clientx', delta: x - startat.clientx }; }), mousemove .pluck('clienty') .skipwhile(function (y) { return math.abs(startat.clienty - y) < 25; }) .map(function (y) { return { prop: 'clienty', delta: y - startat.clienty }; }), //if neither propagates second, subscribe instead mousemove .startwith(startat) .delaysubscription(1000) .tap(function (e) { box.classname = 'press'; prop = 'timestamp'; box.innerhtml = ''; }) .map(function (e) { return { prop: 'timestamp', delta: e.timestamp - startat.timestamp }; })) .takeuntil(mouseup);
edit 1
replaced timeout
moving continuation observable amb
, using delaysubscription
instead.
and here modified version of code:
var box = document.getelementbyid('box'); var mousedown = rx.observable.fromevent(box, 'mousedown'); var mouseup = rx.observable.fromevent(document.body, 'mouseup'); var mousemove = rx.observable.fromevent(box, 'mousemove') .tap(function(e) { e.preventdefault(); }); mousedown.flatmaplatest(function (start) { var startat = start; box.classname = 'waiting'; box.innerhtml = 'waiting...'; return rx.observable.amb( mousemove .pluck('clientx') .skipwhile(function (x) { return math.abs(startat.clientx - x) < 25; }) .map(function (x) { return { prop: 'clientx', delta: x - startat.clientx }; }), mousemove .pluck('clienty') .skipwhile(function (y) { return math.abs(startat.clienty - y) < 25; }) .map(function (y) { return { prop: 'clienty', delta: y - startat.clienty }; }), mousemove .startwith(startat) .delaysubscription(1000) .tap(function (e) { box.classname = 'press'; prop = 'timestamp'; box.innerhtml = ''; }).map(function (e) { return { prop: 'timestamp', delta: e.timestamp - startat.timestamp }; })) .takeuntil(mouseup); }) .subscribe(function (x) { box.innerhtml = x.prop + ': ' + x.delta; }); mouseup.subscribe(function() { box.classname = ''; box.innerhtml = ''; });
body { font: 12px sans-serif; } #box { width: 300px; height: 300px; border: 1px #000 solid; text-align: center; padding: 20px; transition: 0.2s background-color; cursor: pointer; } #box.waiting { background-color: gray; cursor: move; } #box.dragx { background-color: red; cursor: ew-resize; } #box.dragy { background-color: green; cursor: ns-resize; } #box.press { background-color: yellow; cursor: progress; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/2.5.3/rx.all.js"></script> <ol> <li>drag horizontally</li> <li>release</li> <li>drag vertically</li> <li>relase</li> <li>press , hold</li> </ol> <div id="box"></div>
Comments
Post a Comment