更新
我已尝试在此处制作独立版本:https://codepen.io/neezer/pen/pPRJar
它的工作方式与我的本地副本不同,但我希望它足够相似,以便您可以看到我想去的地方。
我没有得到完全相同的行为,因为我将侦听器目标更改为document
,这似乎对某些人有所帮助。
另外,我使用的是RxJS v5和最新版本的React。
仍然掌握着RxJS ......
我有两个Observable:一个在桌面上订阅鼠标悬停x坐标以显示调整大小列,另一个允许用户在该列上拖动。
粗略地说,第一个看起来像这样(以下所有在React组件的componentDidUpdate
生命周期方法中定义):
Rx.DOM.mouseover(tableEl)
.map(/* some complicated x coordinate checking */)
.distinctUntilChanged()
.subscribe(/* setState call */)
这很有效,并且给了我这个:
所以现在我想提供实际的"拖动"行为,我尝试设置一个像这样的新Observable
// `resizerEl` is the black element that appears on hover
// from the previous observable; it's just a div that gets
// repositioned and conditionally created
Rx.DOM.mousedown(resizerEl)
.flatMap(md => {
md.preventDefault()
return Rx.DOM.mousemove(tableEl)
.map(mm => mm.x - md.x)
.takeUntil(Rx.DOM.mouseup(document))
})
.subscribe(/* do column resizing stuff */)
有三个问题:
takeUntil
完成了Observable,我不知道我怎么能重启"它。mousemove
仍处于活动状态,因此一旦我的x
位置变化足以触发该行为,我的黑色div就会消失。
首先注意清洁刷新后我不能拖动手柄(#3),然后刷新,我无法将手柄拖过第一个Observable的边界设置 - 并且黑色缩放器栏消失并重新出现,因为我的鼠标坐标进入并离开该信封(#2)。
我已经在这方面做了很长一段时间了,并且非常感谢我在这里做错了什么。简而言之,我想要
正如我之前提到的,我目前在React组件的componentDidUpdate
生命周期方法中进行了这种逻辑设置,其形状看起来大致如下:
componentWillUpdate() {
// bail if we don't have the ref to our table
if (!tableEl) {
return;
}
// try not to have a new Observable defined on each component update
if (!this.resizerDrag$ && this.resizer) {
this.resizerDrag$ = // second Observable from above
}
// try not to have a new Observable defined on each component update
if (!this.resizerPos$) {
this.resizerPos$ = // first Observable from above
}
}
答案 0 :(得分:0)
我现在已经玩过这个了,我不认为这个答案会完整,但我想分享我的见解。希望一个更先进的RxJS思想会引起人们的注意,我们都可以共同努力解决这个问题:)。
我重新创造了一个" lite-er"在CodePen中的这个版本,使用一些轻微的jQuery操作而不是React。这就是我到目前为止所拥有的:
解决第一点有助于其他两点。根据我必须做的事情来获取resizerEl
,我感觉它是基于render
中的某些内容在组件的this.state
方法中呈现的。如果这是真的,那意味着当第一个observable仍然能够创建和销毁resizerEl
时,即使第二个observable正在侦听。这意味着resizerEl
将无法再生成任何事件,即使观察结果没有完成,直到您被篡改为止。
在我的情况下,我注意到如果你移动鼠标足够快以超出你想要拖动的宽度,它将消除resizerEl
,这是我们想要的,但不是我们和#39;试图拖东西!
我的解决方案:我在"州"中引入了另一个变量。 "组件"。当我们在true
上匆匆而过,然后resizerEl
当我们再次出现时,这会设置为false
。
然后我们使用switchMap
。
Rx.DOM.mousemove(tableEl)
.switchMap(function(event) {
return this.state.mouseIsDown ? Rx.Observable.never() : Rx.Observable.of(event);
})
.map(...
这可能是一种更好的方法,而不仅仅是将event
放回到Observable中,但这是我工作的最后一部分而且我的大脑有点油腻。这里的关键是在鼠标停止时切换到Observable.never
,这样我们就不会在操作员链中向下移动。
实际上, 一件好事是,甚至可能不需要将其放入this.state
,因为这会导致重新渲染。您可以只使用实例变量,因为该变量仅对Observables功能至关重要,而不是任何渲染。因此,使用this.mouseIsDown
也一样好。
我们如何处理鼠标停机或停机?
第1部分:
...
Rx.DOM.mousedown(resizerEl)
.do(() => this.mouseIsDown = true)
最好将其抽象为函数,但这是它所做的事情的要点。
第2部分:
...
return Rx.DOM.mousemove(tableEl)
.map(mm => mm.x - md.x)
.takeUntil(Rx.DOM.mouseup(document))
.doOnCompleted(() => this.mouseIsDown = false)
在observable完成后,我们利用doOnComplete
来执行此副作用,在这种情况下,它将在mouseup
上。
现在这里很棘手,我从未遇到过这个问题。您会看到,Rx.DOM.mousedown(resizerEl)
每次flatMap
发出事件时,都会在return Rx.DOM.mousemove(tableEl)...
内创建新的Observable 。resizerEl
我在制作时使用了RxJS 4.1,因此可能存在行为上的差异,但我发现仅仅因为内部可观察完成并不意味着外部也会完成。
那么可以发生什么?好吧,我想,既然你正在使用React,那么当组件呈现时,resizerEl
分别被创建/销毁。我当然没有看到你的其余代码,但请纠正我如果我对这个假设有误。
这对我来说不是问题,因为为了简单起见,我只是重复使用与拖动器相同的元素,只是在我没有悬停在可拖动元素上时隐藏它。
所以重要的问题是:如何在组件中定义和使用ref
?我假设对它的实际引用是使用Rx.Dom
进行的。但是如果DOM元素被破坏或重新创建,那么componentDidUpdate
绑定需要重复进行。
我看到您正在使用Rx.Dom.mousedown
执行此操作。但是,resizerEl
事件仍可能绑定到引用ref
的旧副本。即使组件破坏DOM中的resizer,并将this.resizer
(我假设为null
)设置为undefined
或this.resizerDrag$
,也不会破坏Observable与该元素绑定。事实上,我甚至不认为它会从内存中删除它,即使它已经从DOM中删除了!这意味着false
永远不会评估为null
/ componentWillUpdate
,并且它仍然会监听不再位于DOM中的元素。
如果是这种情况,if (!this.resizerDrag$ && this.resizer) {
this.resizerDrag$ = // second Observable from above
}
else if (!this.resizer && this.resizerDrag$) {
this.resizerDrag$ = null;
}
中的类似内容可能有所帮助:
Subjects
如果resizer对象不再存在,这将删除Observable,这样我们可以在它返回时正确地重新初始化它。最好处理this.resizer
,保留对一个主题的订阅,只要将主题订阅到不同的mousedown流一旦可用,但让我们保持这个简单:)。 / p>
这是我们必须看到你的代码的其余部分(对于这个组件)告诉你发生了什么,并想出如何解决它。但是,我的假设是,如果{{1}}被删除,你需要故意破坏Observable。
很明白,一旦上述两个问题奏效,这个问题就会消失。很好,很容易!
这是我对这个问题做出的非常天真的模型: https://codepen.io/anon/pen/KmapYZ
沿X轴前后拖动蓝色圆圈。 (有一些小问题和错误与这个问题的范围无关,所以我并不担心它们。)
我当然做了一些细微的改变,只是为了与我使用的更愚蠢的方法保持同步。但是所有的概念都存在,以及你编写的大部分代码都被修改以匹配这种方法。
正如我之前提到的,我没有遇到拖动问题只能工作一次,所以这更好地展示了暂停第一个Observable的解决方案。我重新使用了拖动元素,我认为这就是为什么我没有遇到“只拖动一次”的问题。问题
我希望您或其他任何人可以对此方法进行一些改进,或者向我们展示一种更好(可能更惯用)的方法。