RxJS:区分单击和拖动

时间:2015-10-23 16:10:53

标签: javascript angularjs rxjs

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

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

为了简化问题(并训练自己),我制作了一个滑块指令,基于rx.angular.js拖动' n' drop示例: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&#39; D水平约束到给定范围。

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

我尝试使用delay().takeUntil(mouseup),如另一个SO答案所示,但没有成功。也许也可能需要switch()(以避免使用拖动路径)。

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

2 个答案:

答案 0 :(得分:4)

如果您使用的是ReactiveX / RxJS,则可以使用timeouttimeoutWith

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

如果在超时之前没有发生鼠标,则只会传播空Observable。如果确实如此,则下游观察者将收到一个事件。

答案 1 :(得分:1)

delay(Xms).takeUntil(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$发出&#39; noclick&#39;然后你可以认为没有发生任何点击。

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