【问题标题】:RxJS: Advanced Drag and Drop - Prevent subscription in operatorRxJS:高级拖放 - 防止在操作员中订阅
【发布时间】:2016-09-01 19:50:30
【问题描述】:

我尝试在 RxJS 中实现拖放。我有一个 ID 为 draggable 的 DOM 节点,可以拖动它。通过使用标准程序拖放按预期工作。

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

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

我知道在运算符中订阅远非良好做法,并且可能导致内存泄漏。但我还能做什么?怎样才能做得更好?

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

const target = document.getElementById('draggable');
const mouseup$ = Observable.fromEvent(document, 'mouseup');
const mousedown$ = Observable.fromEvent(target, 'mousedown');
const mousemove$ = Observable.fromEvent(document, 'mousemove');

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

    // Can the next line be avoided? 
    mm$.subscribe(null, null, () => {
      md.target.style.backgroundColor = 'purple';
    });

    return mm$;
  });

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

【问题讨论】:

    标签: drag-and-drop rxjs rxjs5


    【解决方案1】:

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

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

    【讨论】:

    • 感谢您分享您的想法。我在另一篇文章中想出了如何解决你的草稿问题。看到这个小提琴:jsfiddle.net/b11gaewt 感谢您的回复。我会将您的答案标记为解决方案,但建议所有感兴趣的读者也看看我自己的解决方案:stackoverflow.com/a/39397186/434227
    【解决方案2】:

    我一直在努力解决这个问题以找到解决方案。在我的尝试中,我使用了一个组合了 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';
      });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-23
      • 2020-10-07
      • 2021-02-14
      • 1970-01-01
      • 2019-10-27
      • 1970-01-01
      • 2021-12-19
      • 2022-07-31
      相关资源
      最近更新 更多