单击并单击RxJs双击操作
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单击并单击RxJs双击操作相关的知识,希望对你有一定的参考价值。
我有一个RxJs的问题。
当我点击一次时,我需要在console.log中记录一条消息,当我在按钮上单击两次时,我需要记录不同的消息。问题是:
- 在我开始时,如果我第一次点击 - 没有任何反应 - 错误(我应该看到第一条消息'一键')
- 然后,如果我点击按钮然后(一秒钟后)我再次点击,第二次点击我可以看到两条消息 - 错误(我应该看到每个动作只有一条消息)
- 当我单击按钮,等待一秒钟,再次单击,等待一秒钟再次单击,然后在最后一次单击时,我将在console.log中看到三条消息 - 错误(我应该看到每个操作只有一条消息)
- 之后,如果我点击两次(双击),我会看到五条消息双击,一条一条点击 - 错误(我应该只看到一条消息'双击')
我想要的只是:
- 如果我点击一次,我只需要看一条消息('一键')
- 如果我点击两次(双击),仍然只需要看一条消息('双击')
- 如果我点击两次以上 - 没有任何反应
任何帮助?
一个非常简单的答案(不使用任何observable)是使用setTimeout()
并检查每次点击是否已设置超时,如果是,你知道它是在给定时间窗口内的第二次点击(双击),如果没有,它是第一次点击。如果超时到期,您知道只需单击一次,如下所示:
// have a timer that will persist between function calls
private clickTimeout = null;
public test(event): void {
// if timeout exists, we know it's a second click within the timeout duration
// AKA double click
if (this.clickTimeout) {
// first, clear the timeout to stop it from completing
clearTimeout(this.clickTimeout);
// set to null to reset
this.clickTimeout = null;
// do whatever you want on double click
console.log("double!");
} else {
// if timeout doesn't exist, we know it's first click
this.clickTimeout = setTimeout(() => {
// if timeout expires, we know second click was not handled within time window
// so we can treat it as a single click
// first, reset the timeout
this.clickTimeout = null;
// do whatever you want on single click
console.log("one click");
}, 400);
}
}
编辑
我错过了忽略任何超过2次点击的部分。这并没有那么多工作,但是为了能够重用代码,我把它分解了一些,所以它看起来更像。无论如何,要忽略3次以上的点击,它将如下所示:
// count the clicks
private clicks = 0;
private clickTimeout = null;
public test(event): void {
this.clicks++;
if (this.clickTimeout) {
// if is double click, set the timeout to handle double click
if (this.clicks <= 2) {
this.setClickTimeout(this.handleDoubleClick);
} else {
// otherwise, we are at 3+ clicks, use an empty callback to essentially do a "no op" when completed
this.setClickTimeout(() => {});
}
} else {
// if timeout doesn't exist, we know it's first click - treat as single click until further notice
this.setClickTimeout(this.handleSingleClick);
}
}
// sets the click timeout and takes a callback for what operations you want to complete when the
// click timeout completes
public setClickTimeout(cb) {
// clear any existing timeout
clearTimeout(this.clickTimeout);
this.clickTimeout = setTimeout(() => {
this.clickTimeout = null;
this.clicks = 0;
cb();
}, 400);
}
public handleSingleClick() {
console.log("one click");
}
public handleDoubleClick() {
console.log("double!");
}
@Siddharth Ajmeras的答案显示了如何通过事件来做到这一点。我不知道存在dblclick事件。你知道的越多。如果您仍然对如何使用rxjs感兴趣,这里有一个例子。
// How fast does the user has to click
// so that it counts as double click
const doubleClickDuration = 100;
// Create a stream out of the mouse click event.
const leftClick$ = fromEvent(window, 'click')
// We are only interested in left clicks, so we filter the result down
.pipe(filter((event: any) => event.button === 0));
// We have two things to consider in order to detect single or
// or double clicks.
// 1. We debounce the event. The event will only be forwared
// once enough time has passed to be sure we only have a single click
const debounce$ = leftClick$
.pipe(debounceTime(doubleClickDuration));
// 2. We also want to abort once two clicks have come in.
const clickLimit$ = leftClick$
.pipe(
bufferCount(2),
);
// Now we combine those two. The gate will emit once we have
// either waited enough to be sure its a single click or
// two clicks have passed throug
const bufferGate$ = race(debounce$, clickLimit$)
.pipe(
// We are only interested in the first event. After that
// we want to restart.
first(),
repeat(),
);
// Now we can buffer the original click stream until our
// buffer gate triggers.
leftClick$
.pipe(
buffer(bufferGate$),
// Here we map the buffered events into the length of the buffer
// If the user clicked once, the buffer is 1. If he clicked twice it is 2
map(clicks => clicks.length),
).subscribe(clicks => console.log('CLicks', clicks));
这是你想要的或非常接近它:
import { Component, ViewChild, ElementRef } from '@angular/core';
import { fromEvent } from 'rxjs';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
@ViewChild('mybutton') button: ElementRef;
ngAfterViewInit() {
fromEvent(this.button.nativeElement, 'dblclick')
.subscribe(e => console.log('double click'));
fromEvent(this.button.nativeElement, 'click')
.subscribe((e: MouseEvent) => {
if (e.detail === 1) console.log('one click') // could use a filter inside a pipe instead of using an if statement
// if (e.detail === 2) console.log('double click') // an alternative for handling double clicks
});
}
}
和HTML:
<button #mybutton>Test</button>
这个解决方案使用event.detail并且松散地基于这个有用的帖子 - Prevent click event from firing when dblclick event fires - 它不仅讨论了event.detail,还讨论了所涉及的时间问题。
您的代码的一个重要原因是您在多次调用的函数内订阅事件,这意味着每次单击该按钮时您都会创建另一个订阅。使用ngAfterViewInit
(仅作为生命周期的一部分调用一次)可以防止出现此问题(并确保加载DOM)。
这是给你的stackblitz:
https://stackblitz.com/edit/angular-t6cvjj
原谅我的草率类型声明!
这满足您的要求如下:
- 如果我点击一次,我只需要看一条消息('一键') - 通过
- 如果我点击两次(双击),我仍然只需要看一条消息('双击') - 失败,你看到第一次点击'一键',第二次点击'双击'
- 如果我点击两次以上 - 没有任何反应 - 通过
由于SO帖子中讨论的时间问题,你如何解决这个问题是一个偏好问题,因此我决定不解决它。而且,你不付钱给我;)然而,这个答案应该会帮助你完全解决问题。
PS上面有关SO帖子的评论表明event.detail可能无法在IE11上运行
您可以创建一个侦听dblclick
的指令,然后在指令中执行必要的操作。
import { Directive, HostListener } from '@angular/core';
@Directive({
selector: '[appDoubleClick]'
})
export class DoubleClickDirective {
constructor() { }
@HostListener('dblclick') onDoubleClicked() {
console.log('onDoubleClicked ran!');
}
}
模板:
<button appDoubleClick (click)="test($event)">Test</button>
不确定这是否适用于ios。但请试一试。
以上是关于单击并单击RxJs双击操作的主要内容,如果未能解决你的问题,请参考以下文章