You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Observable检测单击与双击:Buffer操作符异常问题问询

Rx.NET 正确识别单击与双击事件流

我有一个IObservable<Click>点击事件流,想要从中区分用户的单击或双击操作。尝试用指定doubleClickTime(如300ms)的Buffer操作符缓冲事件,通过计数判断操作类型,但双击时会同时输出Double Click和Single Click,即同时触发双击和单击逻辑。

现有实现代码:

IObservable<Click> clicks = ...;

IDisposable subscription = clicks.Buffer(clicks, _ => Observable.Timer(doubleClickTime))
.Subscribe(items =>
 {
    if(items.Count > 1)
    {
      Debug.WriteLine($"Double Click");
    }
    else
    {
      Debug.WriteLine($"Single Click");
    }
 });

问题原因:原代码用每次点击作为缓冲窗口的开启信号,双击时第二次点击会开启新的缓冲窗口,导致第一个窗口包含两次点击(输出双击),第二个窗口仅包含第二次点击(输出单击)。

补充:曾参考RxJS的实现示例,但无法准确转译为C#:

const clickOrDoubleClick$ = click$.pipe(
buffer(click$.pipe(switchMap(() => timer(DOUBLE_CLICK_INTERVAL)))),
map((clicksArr) =>
clicksArr.length > 1 ? createDoubleClick() : createClick() ));

解决方案

方法一:转译RxJS示例到C#

对应RxJS的实现逻辑,使用Select+Switch替代RxJS的switchMap,确保每次新点击都会取消之前的定时器,仅保留最新的缓冲窗口:

var doubleClickTime = TimeSpan.FromMilliseconds(300);

IObservable<string> clickOrDoubleClick = clicks
    .Buffer(
        clicks.Select(_ => Observable.Timer(doubleClickTime))
              .Switch() // 每次新点击切换到新定时器,旧定时器被取消
    )
    .Select(items => items.Count > 1 ? "Double Click" : "Single Click");

clickOrDoubleClick.Subscribe(result => Debug.WriteLine(result));

方法二:使用GroupByUntil分组处理

将指定时间窗口内的点击归为一组,超时后关闭分组并判断点击次数:

var doubleClickTime = TimeSpan.FromMilliseconds(300);

var subscription = clicks
    .GroupByUntil(
        _ => 0, // 所有事件归入同一分组
        group => group.Throttle(doubleClickTime) // 超时无新事件则关闭分组
    )
    .SelectMany(group => group.ToList())
    .Subscribe(clickList =>
    {
        if (clickList.Count > 1)
        {
            Debug.WriteLine("Double Click");
        }
        else
        {
            Debug.WriteLine("Single Click");
        }
    });

方法三:Timeout+Amb组合判断

分别定义单击和双击的事件流,再合并去重:

var doubleClickTime = TimeSpan.FromMilliseconds(300);

// 单击流:点击后超时无后续点击则触发
var singleClickStream = clicks
    .SelectMany(click =>
        Observable.Return(click)
                  .Timeout(doubleClickTime, Observable.Empty<Click>())
    )
    .Select(_ => "Single Click");

// 双击流:连续两次点击后触发
var doubleClickStream = clicks
    .Zip(clicks.Skip(1), (first, second) => new[] { first, second })
    .Throttle(doubleClickTime)
    .Select(_ => "Double Click");

// 合并流并去重,避免重复触发
var clickOperationStream = Observable.Merge(singleClickStream, doubleClickStream)
    .DistinctUntilChanged();

clickOperationStream.Subscribe(result => Debug.WriteLine(result));

内容的提问来源于stack exchange,提问作者James B

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 16:40:19