在RxJS 2.2.26版本中
以下代码生成了一个可以发出点击次数的流(双击,三击等)。
var multiClickStream = clickStream
.buffer(function() { return clickStream.throttle(250); })
.map(function(list) { return list.length; })
.filter(function(x) { return x >= 2; });
在RxJS版本4.0.6中</ p>
此代码不再产生所需的结果。
如何在RxJS 4.0.6中获得相同的功能?
答案 0 :(得分:11)
实际上有更好的方法可以通过RxJs找到双击:
var mouseDowns = Rx.Observable.fromEvent(button, "mousedown");
var doubleClicks = mouseDowns.timeInterval()
.scan<number>((acc, val) => val.interval < 250 ? acc + 1 : 0, 0)
.filter(val => val == 1);
这样做的好处是您不需要等待整整250毫秒来识别双击 - 如果用户之间只有120毫秒的点击次数,则双击和结果操作之间会有明显的延迟。
请注意,通过计数(使用.scan())并过滤到第一个计数,我们可以将流限制为双击并忽略之后的每次快速点击 - 因此点击 < em>点击 点击不会导致两次双击。
答案 1 :(得分:9)
万一有人想知道如何用RxJS 5(5.0.0-beta.10)做同样的事情,这就是我的工作方式:
const single$ = Rx.Observable.fromEvent(button, 'click');
single$
.bufferWhen(() => single$.debounceTime(250))
.map(list => list.length)
.filter(length => length >= 2)
.subscribe(totalClicks => {
console.log(`multi clicks total: ${totalClicks}`);
});
我花了很多时间试图解决这个问题,因为我也在学习反应式编程(使用RxJS),所以,如果你看到这个实现有问题或知道更好的方法来做同样的事情,我'我很高兴知道!
答案 2 :(得分:6)
这是我提出的解决方案,它创建了单击或双击流(不再是其他任何内容,因此没有三次点击)。目的是快速连续地检测多次双击,但也接收单击的通知。
let click$ = Observable.fromEvent(theElement, "click");
let trigger$ = click$.exhaustMap(r =>
click$.merge(Observable.timer(250)).take(1));
let multiclick$ = click$.buffer(trigger$).map(r => r.length);
原因是,我们使用buffer
运算符对点击进行分组,并按如下方式设计缓冲区触发事件:每次点击发生时,我们都会在两个可观察对象之间启动种族 :
一旦这场比赛结束(我们使用take(1)
,因为我们只对第一个事件感兴趣),我们会发出缓冲区触发事件。这是做什么的?它使缓冲在第二次点击到达时或250毫秒时停止。
我们使用exhaustMap
运算符来抑制另一场比赛的创建。否则,双击对中的第二次点击会发生这种情况。
答案 3 :(得分:5)
这是另一种观点(如果用户快速点击2 * N次,则产生N次双击):
const clicks = Rx.Observable.fromEvent(document, 'click');
const doubleclicks = clicks.exhaustMap(() =>
clicks.take(1).takeUntil(Rx.Observable.interval(250)),
);
答案 4 :(得分:3)
一个更通用的解决方案:从一个可观察的源开始,当这个observable快速连续两次快速发出完全相同的值时检测并传播:
var scheduler = Rx.Scheduler.default; // replace with TestScheduler for unit tests
var events = // some observable
var doubleClickedEvents = events
/* Collect the current and previous value */
.startWith(null) // startWith null is necessary if the double click happens immediately
.pairwise()
.map(pair => ({ previous: pair[0], current: pair[1] })
/* Time the interval between selections. */
.timeInterval(scheduler)
/* Only accept two events when they happen within 500ms */
.filter(timedPair => timedPair.interval < 500)
.map(timedPair => timedPair.value)
/* Only accept two events when they contain the same value */
.filter(pair => pair.previous && pair.previous === pair.current)
.map(pair => pair.current)
/* Throttle output. This way, triple and quadruple clicks are filtered out */
.throttle(500, scheduler)
与提供的解决方案的区别在于:
答案 5 :(得分:2)
throttle
从RxJS 3开始更改为debounce
我相信。
由于原始命名方案实际上与Rx的其他实现或实际定义不匹配,因此对这种方式存在很大争议。
https://github.com/Reactive-Extensions/RxJS/issues/284
在RxJS 5的重新实现中,它甚至设法让人们更加困惑: https://github.com/ReactiveX/rxjs/issues/480
答案 6 :(得分:1)
找到一种更适合区分单击/双击的方法。 (所以我可以获得单击或双击,但不能同时获得两者)
对于双击,处理快速双击2 * N次产生N个事件:
Observable.prototype.doubleEvent = function(timing = 150, count = 2, evt = this) { /// flexibility to handle multiple events > 2
return this.exhaustMap( () => /// take over tapping event,
evt.takeUntil( Observable.timer(timing) ) /// until time up,
.take(count-1) /// or until matching count.
);
}
注意:参数evt = this
使doubleEvent与startWith()
一起使用,可以正确地使用this
Observable。
用法:
this.onTap
.doubleEvent()
.subscribe( (result) => console.log('double click!') );
区分单击/双击:
Observable.prototype.singleEvent = function(timing = 150) {
return this.mergeMap( /// parallel all single click event
() => Observable.timer(timing) /// fire event when time up
);
}
Observable.prototype.singleOrDoubleEvent = function(timing = 150) {
return this.exhaustMap( (e) => /// take over tapping event
Observable.race( /// wait until single or double event raise
this.startWith(e).doubleEvent(timing, 2, this)
.take(1).mapTo(1),
this.startWith(e).singleEvent(timing)
.take(1).mapTo(0),
)
);
}
用法:
this.onTap
.singleOrDoubleEvent()
.subscribe( (result) => console.log('click result: ', result===1 ? 'double click' : 'single click') );
答案 7 :(得分:1)
此答案的灵感来自@Aviad P。
我相信以下代码是正确的答案。满足所有这些要求。
single-click
,double-click
和triple-click
。triple-clicks
。如果单击5次,将得到一个triple-click
和一个double-click
。等等triple-click
将立即被解雇,无需等待整个时间窗口完成。four-click
,five-click
...
const btn = document.querySelector('#btn');
const click$ = Rx.Observable.fromEvent(btn, "click");
const trigger$ =
click$.exhaustMap(r =>
click$
.take(2)
.last()
.race(click$
.startWith(0)
.debounceTime(500)
.take(1))
);
click$
.buffer(trigger$)
.map(l => l.length)
.map(x => ({
1: 'single',
2: 'double',
3: 'tripple'
}[x]))
.map(c => c + '-click')
.subscribe(log);
Rx.Observable.fromEvent(document.querySelector('#reset'), 'click')
.subscribe(clearLog);
function log(message) {
document.querySelector('.log-content').innerHTML += ('<div>' + message + '</div>');
}
function clearLog() {
document.querySelector('.log-content').innerHTML = null;
}
.log {
padding: 1rem;
background-color: lightgrey;
margin: 0.5rem 0;
}
.log-content {
border-top: 1px solid grey;
margin-top: 0.5rem;
min-height: 2rem;
}
.log-header {
display: flex;
justify-content: space-between;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.7/Rx.js"></script>
<button id='btn'>Click Me</button>
<button type="button" id='reset'> Reset </button>
<div class='log' id='logpanel'>
<div class='log-header'>
Log:
</div>
<div class="log-content"></div>
</div>