OP与官方示例的链接已关闭,它在这里:
https://github.com/Reactive-Extensions/RxJS/blob/master/examples/dragndrop/dragndrop.js
原始问题有两种解决方案,使用在官方示例上扩展的原始鼠标事件,或者使用原生 HTML5 拖放事件。
由“mouseup”、“mousedown”和“mousemove”组成
首先,我们用于mousedown1.switchMapTo(mousemove.takeUntil(mouseup).take(1))
获取“拖动开始”流。switchMapTo
(参见文档)使用扁平化(将“mousedown1”映射到“鼠标移动直到鼠标向上”的第一个发射中的每一个,也称为“拖动”)和switch
(参见文档),为我们提供了最新的mousedown1
发射,然后是拖动,即不仅仅是任何时候你在盒子上点击鼠标。
另一个“mousedown”流mousedown2
用于组合mousedrag
流,因此我们可以在拖动时不断地渲染框。 mousedown1
以上优先mousedown2
。有关更多信息,请参阅https://stackoverflow.com/a/35964479/232288。
一旦我们有了“拖动开始”流,我们就可以通过以下方式获得“拖动停止”流:mousedragstart.mergeMapTo(mouseup.take(1))
。mergeMapTo
(请参阅文档)将“mousedragstart”映射到“mouseup”的每个第一次发射,为我们提供最新的“mousedragstart”,然后是“mouseup”,这本质上是“拖动停止”。
下面的演示适用于 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,
];
}
.box {
position: relative;
width: 150px;
height: 150px;
background: seagreen;
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.3/Rx.js"></script>
<div class="box"></div>
<h3 id="events"></h3>
由 HTML5 原生 'dragstart'、'dragover' 和 'drop' 组成
本机事件使事情变得更容易一些。只需确保拖动观察者调用e.preventDefault()
,以便 body 元素可以成为有效的放置区域。查看更多信息。
我们以与上面使用方式类似的方式使用switchMap
(参见文档switchMapTo
) :展平并将最新的“dragstart”映射到最新的“drop”以获取我们的“drag then drop”流。
不同的是,只有当用户放下 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';
});
.box {
position: relative;
width: 150px;
height: 150px;
background: seagreen;
cursor: pointer;
}
<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>