Rx - 跟踪鼠标平移操作

Rx - Keeping track of a mouse panning operation

我正在实施点击和拖动类型的操作,以便在图形应用程序中平移相机。

我想跟踪我们是否在另一个流中平移,可以由其他操作检查以进行过滤(例如,如果我们在平移,我们不应该允许橡皮筋选择)。

代码看起来像这样:

MouseClicked
    .Where(startClick => startClick.Action == MouseAction.LeftDown)
    .SelectMany(_ =>
        MouseMoved
            .Select(endMove => (_.startClick, endMove))
            .TakeUntil(MouseReleased))
    .Subscribe(_ => PanCamera(_.startClick, _.endMove));

我的解决方法是添加下面两行代码

MouseClicked
    .Where(startClick => startClick.Action == MouseAction.LeftDown)
    .Do(_ => _isPanning.OnNext(true))  // Add this
    .SelectMany(_ =>
        MouseMoved
            .Select(endMove => (_.startClick, endMove))
            .TakeUntil(MouseReleased)
            .Finally(() => _isPanning.OnNext(false))   // Add this
    )
    .Subscribe(_ => PanCamera(_.startClick, _.endMove));

其中 _isPanningSubject。这很好用,但我想知道是否有更好的方法,而不必使用 Subject.

您可以将平移状态折叠到具有多个订阅者的单个可观察对象中,而不是使用主题。例如:

public IDisposable PanningBehaviour()
{
    var observable = MouseClicked
        .Where(startClick => startClick.Action == MouseAction.LeftDown)
        .SelectMany(start =>
            MouseMoved
                .Select(current => (Start: start.Position, Current: current.Position, Panning: PanningState.Panning))
                .TakeUntil(MouseReleased)
                .Concat(Observable.Return((Start: Point.Empty, Current: Point.Empty, Panning: PanningState.NotPanning))))
        .Publish();

    var cameraSubscription = observable
        .Where(tuple => tuple.Panning == PanningState.Panning)
        .Subscribe(tuple => PanCamera(tuple.Start, tuple.Current));

    var notPanningSubscription = observable
        .Select(tuple => tuple.Panning == PanningState.NotPanning)
        .Subscribe(allowOperations => { /* Allow / disallow actions */ });

    return new CompositeDisposable(
        notPanningSubscription,
        cameraSubscription,
        observable.Connect()
    );
}

/* Allow / disallow actions */中你可以放任何你喜欢的东西。在 MVVM 应用程序中,这种方法特别适用于允许外部代码控制其 "CanExecute" 状态的 ICommand 实现。

FWIW,我的MVx.Observable package features an Observable.Command可以直接订阅"notPanning"状态如下图:

private MVx.Observable.Command _allowSelection = new MVx.Observable.Command();

public IDisposable PanningBehaviour()
{
    var observable = MouseClicked
        .Where(startClick => startClick.Action == MouseAction.LeftDown)
        .SelectMany(start =>
            MouseMoved
                .Select(current => (Start: start.Position, Current: current.Position, Panning: PanningState.Panning))
                .TakeUntil(MouseReleased)
                .Concat(Observable.Return((Start: Point.Empty, Current: Point.Empty, Panning: PanningState.NotPanning))))
        .Publish();

    var cameraSubscription = observable
        .Where(tuple => tuple.Panning == PanningState.Panning)
        .Subscribe(tuple => PanCamera(tuple.Start, tuple.Current));

    var notPanningSubscription = observable
        .Select(tuple => tuple.Panning == PanningState.NotPanning)
        .Subscribe(_allowSelection);

    return new CompositeDisposable(
        notPanningSubscription,
        cameraSubscription,
        observable.Connect()
    );
}

public ICommand AllowSelection => _allowSelection;

希望对您有所帮助。