鉴于: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'上收到回调?
答案 0 :(得分:2)
OP官方示例的链接已关闭,它在这里:
https://github.com/Reactive-Extensions/RxJS/blob/master/examples/dragndrop/dragndrop.js
原始问题有两种解决方案,使用扩展到官方示例的原始鼠标事件或原生HTML5拖放事件。
首先我们使用mousedown1.switchMapTo(mousemove.takeUntil(mouseup).take(1))
来启动&#39;拖动开始&#39;流。 switchMapTo
(see doc)使用扁平化的组合(映射&#39; mousedown1&#39;对每个鼠标移动的第一个发射,直到鼠标移动&#39;,又称&# 39;拖动&#39;)和switch
(see doc),向我们提供最新的mousedown1
点击,然后拖动,即不是在盒子上单击鼠标时。
另一个&#39; mousedown&#39;流mousedown2
用于组成mousedrag
流,以便我们可以在拖动时不断渲染框。上面的mousedown1
优先于mousedown2
。有关详细信息,请参阅https://stackoverflow.com/a/35964479/232288。
一旦我们开始了我们的“开始”操作。流,我们可以得到“阻止停止&#39;流来自:mousedragstart.mergeMapTo(mouseup.take(1))
。 mergeMapTo
(see doc)地图&#39; mousedragstart&#39;对于每个第一次发出的'mouseup',给我们最新的&mousedragstart&#39;其次是&#39; mouseup&#39;这实际上是“拖延”#。
以下演示适用于RxJS v5:
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;
原生事件使事情变得容易一些。只需确保dragover观察者调用e.preventDefault()
,以便body元素可以成为有效的放置区域。 See more info
我们使用switchMap
(see doc)的方式与上面使用switchMapTo
的方式类似:展平并映射最新的&#39; dragstart&#39;最新的&#39; drop&#39;得到我们的拖累然后放弃&#39;流。
区别仅在用户放弃div时我们是否更新位置。
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;