在RxJS中创建一个可观察的“已完成”事件

时间:2014-06-13 21:45:52

标签: javascript reactive-programming rxjs

鉴于:the reactive extensions drag and drop example,您如何只订阅丢弃事件?

我修改了代码以订阅“已完成”的回调,但它没有完成。

    (function (global) {

    function main () {
        var dragTarget = document.getElementById('dragTarget');
        var $dragTarget = $(dragTarget);

        // Get the three major events
        var mouseup  = Rx.Observable.fromEvent(document, 'mouseup');
        var mousemove = Rx.Observable.fromEvent(document,    'mousemove');
        var mousedown = Rx.Observable.fromEvent(dragTarget, 'mousedown');

        var mousedrag = mousedown
            .filter(function(md){                   
                //console.log(md.offsetX + ", " + md.offsetY);
                return  md.offsetX <= 100
                        ||
                        md.offsetY <= 100;
            })
            .flatMap(function (md) {

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

                // Calculate delta with mousemove until mouseup
                return mousemove.map(function (mm) {
                    mm.preventDefault();

                    return {
                        left: mm.clientX - startX,
                        top: mm.clientY - startY
                    };
                }).takeUntil(mouseup);
            });


        // Update position
        var subscription = mousedrag.subscribe(
        function (pos) {                    
            dragTarget.style.top = pos.top + 'px';
            dragTarget.style.left = pos.left + 'px';
        },
        function(errorToIgnore) {},
        function() {    alert('drop');});

    }

    main();

}(window));

我已经读过热的可观察量,例如那些从鼠标事件中创建的,从未“完成”。它是否正确?我怎么能在'drop'上收到回调?

1 个答案:

答案 0 :(得分:2)

OP官方示例的链接已关闭,它在这里:

https://github.com/Reactive-Extensions/RxJS/blob/master/examples/dragndrop/dragndrop.js

原始问题有两种解决方案,使用扩展到官方示例的原始鼠标事件或原生HTML5拖放事件。

来自&#39; mouseup&#39;,&#39; mousedown&#39;和&#39; mousemove&#39;

首先我们使用mousedown1.switchMapTo(mousemove.takeUntil(mouseup).take(1))来启动&#39;拖动开始&#39;流。 switchMapTosee doc)使用扁平化的组合(映射&#39; mousedown1&#39;对每个鼠标移动的第一个发射,直到鼠标移动&#39;,又称&# 39;拖动&#39;)和switchsee doc),向我们提供最新的mousedown1点击,然后拖动,即不是在盒子上单击鼠标时。

另一个&#39; mousedown&#39;流mousedown2用于组成mousedrag流,以便我们可以在拖动时不断渲染框。上面的mousedown1优先于mousedown2。有关详细信息,请参阅https://stackoverflow.com/a/35964479/232288

一旦我们开始了我们的“开始”操作。流,我们可以得到“阻止停止&#39;流来自:mousedragstart.mergeMapTo(mouseup.take(1))mergeMapTosee doc)地图&#39; mousedragstart&#39;对于每个第一次发出的'mouseup',给我们最新的&mousedragstart&#39;其次是&#39; mouseup&#39;这实际上是“拖延”#。

以下演示适用于RxJS v5:

&#13;
&#13;
const { fromEvent } = Rx.Observable;
const target = document.querySelector('.box');
const events = document.querySelector('#events');

const mouseup = fromEvent(target, 'mouseup');
const mousemove = fromEvent(document, 'mousemove');
const [mousedown1, mousedown2] = prioritisingClone(fromEvent(target, 'mousedown'));

const mousedrag = mousedown2.mergeMap((e) => {

  const startX = e.clientX + window.scrollX;
  const startY = e.clientY + window.scrollY;
  const startLeft = parseInt(e.target.style.left, 10) || 0;
  const startTop = parseInt(e.target.style.top, 10) || 0;

  return mousemove.map((e2) => {
    e2.preventDefault();

    return {
      left: startLeft + e2.clientX - startX,
      top: startTop + e2.clientY - startY
    };
  }).takeUntil(mouseup);
});

// map the latest mouse down emit to the first mouse drag emit, i.e. emits after pressing down and
// then dragging.
const mousedragstart = mousedown1.switchMapTo(mousemove.takeUntil(mouseup).take(1));

// map the mouse drag start stream to first emit of a mouse up stream, i.e. emits after dragging and
// then releasing mouse button.
const mousedragstop = mousedragstart.mergeMapTo(mouseup.take(1));

mousedrag.subscribe((pos) => {
  target.style.top = pos.top + 'px';
  target.style.left = pos.left + 'px';
});

mousedragstart.subscribe(() => {
  console.log('Dragging started');
  events.innerText = 'Dragging started';
});

mousedragstop.subscribe(() => {
  console.log('Dragging stopped');
  events.innerText = 'Dragging stopped';
});

function prioritisingClone(stream$) {
  const first = new Rx.Subject();
  const second = stream$.do(x => first.next(x)).share();

  return [
    Rx.Observable.using(
      () => second.subscribe(() => {}),
      () => first
    ),
    second,
  ];
}
&#13;
.box {
  position: relative;
  width: 150px;
  height: 150px;
  background: seagreen;
  cursor: pointer;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.3/Rx.js"></script>
<div class="box"></div>

<h3 id="events"></h3>
&#13;
&#13;
&#13;

从HTML5本地&#39; dragstart&#39;撰写,&#39; dragover&#39;和&#39; drop&#39;

原生事件使事情变得容易一些。只需确保dragover观察者调用e.preventDefault(),以便body元素可以成为有效的放置区域。 See more info

我们使用switchMapsee doc)的方式与上面使用switchMapTo的方式类似:展平并映射最新的&#39; dragstart&#39;最新的&#39; drop&#39;得到我们的拖累然后放弃&#39;流。

区别仅在用户放弃div时我们是否更新位置。

&#13;
&#13;
const { fromEvent } = Rx.Observable;
const target = document.querySelector('.box');
const events = document.querySelector('#events');

const dragstart = fromEvent(target, 'dragstart');
const dragover = fromEvent(document.body, 'dragover');
const drop = fromEvent(document.body, 'drop');

const dragthendrop = dragstart.switchMap((e) => {
  const startX = e.clientX + window.scrollX;
  const startY = e.clientY + window.scrollY;
  const startLeft = parseInt(e.target.style.left, 10) || 0;
  const startTop = parseInt(e.target.style.top, 10) || 0;
  // set dataTransfer for Firefox
  e.dataTransfer.setData('text/html', null);
  console.log('Dragging started');
  events.innerText = 'Dragging started';

  return drop
    .take(1)
    .map((e2) => {
      return {
        left: startLeft + e2.clientX - startX,
        top: startTop + e2.clientY - startY
      };
    });
});

dragover.subscribe((e) => {
  // make it accepting drop events
  e.preventDefault();
});

dragthendrop.subscribe((pos) => {
  target.style.top = `${pos.top}px`;
  target.style.left = `${pos.left}px`;
  console.log('Dragging stopped');
  events.innerText = 'Dragging stopped';
});
&#13;
.box {
  position: relative;
  width: 150px;
  height: 150px;
  background: seagreen;
  cursor: pointer;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.3/Rx.js"></script>
<div class="box" draggable="true"></div>

<h3 id="events"></h3>
&#13;
&#13;
&#13;