最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to handle mouse and touch events simultaneously with reactive event streams - Stack Overflow

programmeradmin6浏览0评论

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!

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!

Share Improve this question edited Jun 21, 2015 at 21:13 Achrome 7,82114 gold badges38 silver badges45 bronze badges asked Jun 21, 2015 at 21:07 Jesse HattabaughJesse Hattabaugh 7,9668 gold badges36 silver badges39 bronze badges 1
  • 2 One thing that you should look to avoid: adding subscriptions inside onValue handlers like you do here. It is an anti-pattern, the correct way to do this is to use a binator (here it's flatMap) – OlliM Commented Jun 22, 2015 at 7:12
Add a ment  | 

2 Answers 2

Reset to default 18

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./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare./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';
    });
发布评论

评论列表(0)

  1. 暂无评论