【问题标题】:RxJS: distinguish single click from dragRxJS:区分单击和拖动
【发布时间】:2015-10-23 16:10:52
【问题描述】:

我有一个 AngularJS 组件,它应该对单击或拖动(调整区域大小)做出反应。

我开始在我的应用程序中使用 RxJS (ReactiveX),所以我尝试找到使用它的解决方案。请求的 Angular 方面是次要的......

为了简化问题(并训练自己),我制作了一个滑块指令,基于 rx.angular.js 拖放示例:http://plnkr.co/edit/UqdyB2 请参阅 Slide.js 文件(其他代码用于其他实验)。这个逻辑的代码是:

    function(scope, element, attributes)
    {
      var thumb = element.children(0);
      var sliderPosition = element[0].getBoundingClientRect().left;
      var sliderWidth = element[0].getBoundingClientRect().width;
      var thumbPosition = thumb[0].getBoundingClientRect().left;
      var thumbWidth = thumb[0].getBoundingClientRect().width;

      // Based on drag'n'drop example of rx-angular.js
      // Get the three major events
      var mousedown = rx.Observable.fromEvent(thumb,     'mousedown');
      var mousemove = rx.Observable.fromEvent(element,   'mousemove');
      var mouseup   = rx.Observable.fromEvent($document, 'mouseup');

      // I would like to be able to detect a single click vs. click and drag.
      // I would say if we get mouseup shortly after mousedown, it is a single click;
      // mousedown.delay(200).takeUntil(mouseup)
      //   .subscribe(function() { console.log('Simple click'); }, undefined, function() { console.log('Simple click completed'); });

      var locatedMouseDown = mousedown.map(function (event) 
      {
        event.preventDefault();
        // console.log('Click', event.clientX - sliderPosition);
        // calculate offsets when mouse down
        var initialThumbPosition = thumb[0].getBoundingClientRect().left - sliderPosition;
        return { from: initialThumbPosition, offset: event.clientX - sliderPosition };
      });

      // Combine mouse down with mouse move until mouse up
      var mousedrag = locatedMouseDown.flatMap(function (clickInfo) 
      {
        return mousemove.map(function (event)
        {
          var move = event.clientX - sliderPosition - clickInfo.offset;
          // console.log('Move', clickInfo);
          // calculate offsets from mouse down to mouse moves
          return clickInfo.from + move;
        }).takeUntil(mouseup);
      });

      mousedrag 
        .map(function (position)
        {
          if (position < 0)
            return 0;
          if (position > sliderWidth - thumbWidth)
            return sliderWidth - thumbWidth;
          return position;
        })
        .subscribe(function (position) 
        {
          // console.log('Drag', position);
          // Update position
          thumb.css({ left: position + 'px' });
        });
    }

这主要是横向限制在给定范围内的 D'n'D。

现在,我想听一下 mousedown,如果鼠标在短时间内(比如 200 毫秒,进行调整)发生,我将其视为单击并进行特定处理(例如,将位置重置为零)。

我尝试使用delay().takeUntil(mouseup),如另一个 SO 答案中所见,但没有成功。也许还需要一个 switch() (以避免走拖拉路线)。

有什么想法吗?提前致谢。

【问题讨论】:

    标签: javascript angularjs rxjs


    【解决方案1】:

    你可以使用timeouttimeoutWith,如果你使用的是 ReactiveX/RxJS)

    var click$ = mousedown.flatMap(function (md) {
      return mouseup.timeoutWith(200, Observable.empty());
    });
    

    如果 mouseup 在超时之前没有发生,它只会传播一个空的Observable。如果是,那么下游观察者将收到一个事件。

    【讨论】:

    • 它适用于timeout()。我在 v.4.0 的 RxJS 库中没有找到 timeoutWith,但显然有一个 timeoutWithSelector 仅在 lite 子库中可用。它似乎与timeout 的第二个版本做同样的事情。也许这只是一个遗产。无论如何,它有效。所以谢谢。
    • @PhiLho timeoutWithRxJS vNext 的一部分。 RxJS 4.0 仍在 Reactive-Extensions 项目下,所以它使用timeout。你提到了 ReactiveX,这就是我提到它的原因。但很乐意提供帮助!
    • vNext?叹。名称和 ESNext 一样愚蠢... ;-) 每个版本都是“下一个”,不知何故...我知道这是为了标记重大更新/更改,但是,名称仍然是无意义的。我想 vNext 最终会是 v.5 吗?不管怎样,你把我推向了正确的方向(如果你愿意,请参阅 Plunker 的更新),再次感谢。
    【解决方案2】:

    delay(Xms).takeUntil(mouseup) 的诀窍不是与您想要的相反吗?我的意思是,您想在倒计时之前检测 mouseup 事件何时发生,而上述技巧检测 mouseup 事件何时发生在倒计时之后。

    我会在这些方面尝试一些东西(目前尚未测试,但希望它能让你朝着积极的方向前进):

    var click$ = mousedown.flatMap(function ( mouseDownEv ) {
      return merge(
          Rx.just(mouseDownEv).delay(Xms).map(function ( x ) {return {event : 'noclick'};}),
          mouseup.map(function ( mouseUpEv ) {return {event : mouseUpEv};})
          ).first();
    });
    

    我们的想法是让mouseup 事件与延迟后发生的虚拟发射比赛,看看谁赢了。因此,如果click$ 发出“noclick”,那么您可以认为没有发生点击。

    希望这有效,我会尽快测试,但如果你在我之前测试,请告诉我。

    【讨论】:

    • 我还没有尝试过(还没有?),因为我首先尝试了@paulpdniels 的答案,它更简单......并且有效。但我很欣赏这个答案,它展示了一种有趣的技术。谢谢。
    • 当然,你选择的答案无疑是最好的,我不知道那个运营商,他们太多了,你很难理解所有这些
    • +1,这就是我问问题的原因,因为我必须让我的头脑适应 Rx 的思维方式......一旦你掌握了尝试回答问题也是探索 API 的好方法基地... :-D
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-19
    • 2013-11-24
    • 2016-02-10
    • 1970-01-01
    • 2019-09-30
    相关资源
    最近更新 更多