RxJS:高级拖放 - 阻止运营商订阅

时间:2016-09-01 19:50:30

标签: drag-and-drop rxjs rxjs5

我尝试在RxJS中实现拖放。我有一个标识为$average = array_sum(array_column($data->data, 'AdjClose')) / count($data->data) 的DOM节点,可以拖动它。通过使用标准过程,拖放按预期工作。

但是我试图增强拖放功能,这就是事情变得复杂的地方。我会尝试在拖动开始后更改元素的背景颜色,并在删除后将其更改回来。

在我的方法中,我使用draggable将鼠标移动事件的结果映射到我的observable中,该事件由鼠标按下事件触发。但是因为我使用鼠标按下事件来完成switchMap ed observable(下面示例中的switchMap),所以我没有机会收到有关内部可观察事件的完成事件的通知,除非我订阅在mm$运营商中。

我知道在运营商中订阅远非良好做法,可能会导致内存泄漏。但我还能做些什么呢?如何做得更好?

小提琴:https://jsfiddle.net/djwfyxs5/

switchMap

2 个答案:

答案 0 :(得分:1)

我在这里回答了类似的问题:RxJs: Drag and Drop example : add mousedragstart

根据你的目的调整答案应该是相当简单的,因为流仍然包含暴露它们被引发的元素的事件。

答案 1 :(得分:0)

我一直在努力寻找解决方案。在我的尝试中,我使用了一个包含mousedownmouseup事件的帮助器observable。通过将它们与combineLatest运算符组合,可以访问mousedown事件的最新值,其中包含已单击的项目(目标)。

这允许我正确设置颜色而不在临时可观察的订阅中,如问题中所示。我的解决方案可以访问in this fiddle

我不确定是否可以使用相同的想法更好地/使用更少的代码。如果可能的话,我很高兴看到改进的实施。

完整代码:

const targets = document.getElementsByClassName('draggable');
const arrTargets = Array.prototype.slice.call(targets);

const mouseup$ = Rx.Observable.fromEvent(document, 'mouseup');
const mousedown$ = Rx.Observable.fromEvent(targets, 'mousedown');
const mousemove$ = Rx.Observable.fromEvent(document, 'mousemove');

// md      -------x------------------------------------------
// mm      xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// mu      -----------------------------------------x--------
// move$   -------xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx---------
// s$      -------x---------------------------------x--------

const s$ = Rx.Observable.combineLatest(
    mousedown$,
  mouseup$.startWith(null), // start immediately
    (md, mu) => {
    const { target } = md; // Target is always the one of mousedown event.
    let type = md.type;
    // Set type to event type of the newer event.
    if (mu && (mu.timeStamp > md.timeStamp)) {
        type = mu.type;
    }
    return { target, type };
    }
);

const move$ = mousedown$
  .switchMap(md => {
    const { offsetX: startX, offsetY: startY } = md;
    const mm$ = mousemove$
      .map(mm => {
        mm.preventDefault();
        return { 
          left: mm.clientX - startX,
          top: mm.clientY - startY,
          event: mm
        };
      })
      .takeUntil(mouseup$);
    return mm$;
  });

Rx.Observable.combineLatest(
    s$, move$.startWith(null), 
  (event, move) => {
    let newMove = move || {};
    // In case we have different targets for the `event` and
    // the `move.event` variables, the user switched the
    // items OR the user moved the mouse too fast so that the
    // event target is the document. 
    // In case the user switched to another element we want 
    // to ensure, that the initial position of the currently 
    // selected element is used.
    if (move && move.event.target !== event.target && arrTargets.indexOf(move.event.target) > -1) {
        const rect = event.target.getBoundingClientRect();
        newMove = {
        top: rect.top, // + document.body.scrollTop,
            left: rect.left // + document.body.scrollLeft
      };
    }
    return { event, move: newMove }
  }
)
  .subscribe(action => {
    const { event, move } = action;
    if (event.type === 'mouseup') {
        event.target.style.backgroundColor = 'purple';
      event.target.classList.remove('drag');
    } else {
        event.target.style.backgroundColor = 'red';
      event.target.classList.add('drag');
    }
    event.target.style.top = move.top + 'px';
    event.target.style.left = move.left + 'px';
  });