简体   繁体   中英

How to handle mouse and touch events simultaneously with reactive event streams

I'm building an audio playback control that lets users scrub back and forth through an audio file. It needs to work with touch and mouse events. How should I go about managing the events for this with reactive event streams?

Here's a rough idea of how I would expect to build it.

<div id="timeline">
  <span id="scrubber"></span>
</div>

then, using Bacon.js to create event streams

var mousedowns = $('#timeline').asEventStream('mousedown');
var touchstarts = $('#timeline').asEventStream('touchstart');

var starts = Bacon.mergeAll(mousedowns, touchstarts);

var mousemoves = $('#timeline').asEventStream('mousemove');
var touchmoves = $('#timeline').asEventStream('touchmove');

var moves = Bacon.mergeAll(mousemoves, touchmoves);

var mouseups = $('#timeline').asEventStream('mouseup');
var touchends = $('#timeline').asEventStream('touchend');

var ends = Bacon.mergeAll(mouseups, touchends);

starts.onValue(function () {
  var repositionScrubber = moves.onValue(function (ev) {
    $('#scrubber').moveTo(ev.offsetX);
  });
  ends.onValue(function () {
    repositionScrubber.stop();
  });
});

I'm sure that's all sorts of wrong, but I'm really new to handling events with observable streams and I don't know of any good cookbooks for it yet. Any help will be appreciated!

This is essentially the canonical drag and drop recipe .

The minimum working example in RxJS is something like this:

var $timeline = $('#timeline');
var $scrubber = $('#scrubber');

var mouseDown = Rx.Observable.merge(
  Rx.Observable.fromEvent($timeline, 'mousedown'),
  Rx.Observable.fromEvent($timeline, 'touchstart'));

var mouseUp = Rx.Observable.merge(
  Rx.Observable.fromEvent($timeline, 'mouseup'),
  Rx.Observable.fromEvent($timeline, 'touchend'));

var mouseMove = Rx.Observable.merge(
  Rx.Observable.fromEvent($timeline, 'mousemove'),
  Rx.Observable.fromEvent($timeline, 'touchmove'));

var subscription = mouseDown.flatMapLatest(function(md) {

  // calculate offsets when mouse down
  var startX = md.offsetX;

  return mouseMove.takeUntil(mouseUp)
                  .map(function(mm) {
                       mm.preventDefault();

                       return {
                        left: mm.clientX - startX,
                       };
                  });
})
.subscribe(function(e) {
  $scrubber.css(e);
});

 var $timeline = $('#timeline'); var $scrubber = $('#scrubber'); var mouseDown = Rx.Observable.merge( Rx.Observable.fromEvent($timeline, 'mousedown'), Rx.Observable.fromEvent($timeline, 'touchstart')); var mouseUp = Rx.Observable.merge( Rx.Observable.fromEvent($timeline, 'mouseup'), Rx.Observable.fromEvent($timeline, 'touchend')); var mouseMove = Rx.Observable.merge( Rx.Observable.fromEvent($timeline, 'mousemove'), Rx.Observable.fromEvent($timeline, 'touchmove')); var subscription = mouseDown.flatMapLatest(function(md) { // calculate offsets when mouse down var startX = md.offsetX; return mouseMove.takeUntil(mouseUp) .map(function(mm) { mm.preventDefault(); return { left: mm.clientX - startX, }; }); }) .subscribe(function(e) { $scrubber.css(e); }); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/2.5.3/rx.all.js"></script> <div id="timeline" style="height: 100px; width: 100px; background: yellow; position: absolute;"> <span id="scrubber" style="height: 20px; width: 30px; background: green; position: relative;">Foo</span> </div> 

var mousemove = Rx.Observable.merge(
            Rx.Observable.fromEvent(document, 'mousemove')
                .map((e) => { e.preventDefault(); return e; }),
            Rx.Observable.fromEvent(document, 'touchmove'))
                .map((e) => e.touches[0]),
        mouseup = Rx.Observable.merge(
            Rx.Observable.fromEvent(dragTarget, 'mouseup'),
            Rx.Observable.fromEvent(dragTarget, 'touchend')),
        mousedown = Rx.Observable.merge(
            Rx.Observable.fromEvent(dragTarget, 'mousedown'),
            Rx.Observable.fromEvent(dragTarget, 'touchstart')
                .map((e) => {
                    var rect = e.target.getBoundingClientRect();
                    e.offsetX = e.touches[0].pageX - rect.left;
                    e.offsetY = e.touches[0].pageY - rect.top;
                    return e;
                }));

mousedown
    .flatMap((start) => mousemove
        .map((mm) =>({
            left: mm.clientX - start.offsetX,
            top: mm.clientY - start.offsetY
        }))
        .takeUntil(mouseup))
    .subscribe(function (pos) {
        dragTarget.style.top = pos.top + 'px';
        dragTarget.style.left = pos.left + 'px';
    });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM