我尝试在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
答案 0 :(得分:1)
我在这里回答了类似的问题:RxJs: Drag and Drop example : add mousedragstart
根据你的目的调整答案应该是相当简单的,因为流仍然包含暴露它们被引发的元素的事件。
答案 1 :(得分:0)
我一直在努力寻找解决方案。在我的尝试中,我使用了一个包含mousedown
和mouseup
事件的帮助器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';
});