RxJS:锁定的水平或垂直鼠标拖动

RxJS: Locked horizontal or vertical mouse dragging

本文关键字:垂直 鼠标 拖动 水平 锁定 RxJS      更新时间:2023-09-26

我想构建一个界面,在这个界面中,您可以在一定距离后选择一个模式,向各个方向拖动。例如,如果你水平拖动25px,它会锁定到该模式,并一直停留在那里,直到你释放鼠标。如果你垂直拖动,它也会做同样的事情。如果长时间点击或按住,可能会发生其他操作。

下面是我的目标的简化小提琴:https://jsfiddle.net/ud37p0y2/2/

反应式编程似乎非常适合这一点,但我似乎不知道如何启动这些模式,然后一直坚持到你松开鼠标。我的出发点是很多拖放的例子,但我似乎无法更进一步。。

某些代码(TypeScript):

var mouseDown = Rx.Observable.fromEvent($element[0], 'mousedown').select((event: MouseEvent): IPoint => {
    event.preventDefault();
    return { x: event.clientX, y: event.clientY };
});
var mouseUp   = Rx.Observable.fromEvent($element[0], 'mouseup');
var mouseMove = Rx.Observable.fromEvent($element[0], 'mousemove');
var mouseDrag = mouseDown.selectMany((mouseDownPos: IPoint) => {
    return mouseMove.select((event: MouseEvent) => {
        return {
            x: event.clientX - mouseDownPos.x,
            y: event.clientY - mouseDownPos.y
        };
    }).takeUntil(mouseUp);
});
var horizontalDrag = mouseDrag.filter((pos: IPoint) => {
    return pos.x < -25 || pos.x > 25;
});
// How would i continue from here?
horizontalDrag.subscribe((pos: IPoint) => {
    console.log('drag'); // This fires all the time, i'd like to do it once when the mode starts and then something else to be called every time the mouse has moved
});

从这里,我想得到一个水平拖动、垂直拖动和保持事件的可观测值。在一个模式启动后,其他模式应该被禁用,例如拖动不会触发长按事件。

我将使用amb+skipWhile的组合。

  • CCD_ 3会给你锁定状态的行为
  • skipWhile将阻止事件启动,直到它们通过阈值一段时间

核心逻辑看起来是这样的:

//Waits for either X or Y to emit then only propagates that one
return Rx.Observable.amb(
    mouseMove
    .pluck('clientX')
    //Wait until the threshold is reached
    .skipWhile(function (x) {
        return Math.abs(startAt.clientX - x) < 25;
    })
    //Transform the outgoing event
    .map(function (x) {
        return {
            prop: 'clientX',
            delta: x - startAt.clientX
        };
    }),
    mouseMove
    .pluck('clientY')
    .skipWhile(function (y) {
        return Math.abs(startAt.clientY - y) < 25;
    })
    .map(function (y) {
        return {
            prop: 'clientY',
            delta: y - startAt.clientY
        };
    }),
    //If neither propagates for a second, then subscribe to this instead
    mouseMove
    .startWith(startAt)
    .delaySubscription(1000)
    .tap(function (e) {
         box.className = 'press';
         prop = 'timeStamp';
         box.innerHTML = '';
    })
    .map(function (e) {
         return {
            prop: 'timeStamp',
            delta: e.timeStamp - startAt.timeStamp
         };
    }))
    .takeUntil(mouseUp);

编辑1

通过将延续Observable移动到amb并使用delaySubscription来替换timeout

这是你的代码的完全修改版本:

var box = document.getElementById('box');
var mouseDown = Rx.Observable.fromEvent(box, 'mousedown');
var mouseUp = Rx.Observable.fromEvent(document.body, 'mouseup');
var mouseMove = Rx.Observable.fromEvent(box, 'mousemove')
.tap(function(e) { e.preventDefault(); });
mouseDown.flatMapLatest(function (start) {
    var startAt = start;
    
    box.className = 'waiting';
    box.innerHTML = 'waiting...';
    return Rx.Observable.amb(
        mouseMove
        .pluck('clientX')
        .skipWhile(function (x) {
            return Math.abs(startAt.clientX - x) < 25;
        })
        .map(function (x) {
            return {
                prop: 'clientX',
                delta: x - startAt.clientX
            };
        }),
    
        mouseMove
        .pluck('clientY')
        .skipWhile(function (y) {
            return Math.abs(startAt.clientY - y) < 25;
        })
        .map(function (y) {
            return {
                prop: 'clientY',
                delta: y - startAt.clientY
            };
        }),
        mouseMove
        .startWith(startAt)
        .delaySubscription(1000)
        .tap(function (e) {
          box.className = 'press';
          prop = 'timeStamp';
          box.innerHTML = '';
        }).map(function (e) {         
          return {
           prop: 'timeStamp',
           delta: e.timeStamp - startAt.timeStamp
          };
        }))
        .takeUntil(mouseUp);
})
.subscribe(function (x) {
    box.innerHTML = x.prop + ': ' + x.delta;
});
mouseUp.subscribe(function() {
    box.className = '';
    box.innerHTML = '';
});
body {
    font: 12px sans-serif;
}
#box {
    width: 300px;
    height: 300px;
    border: 1px #000 solid;
    text-align: center;
    padding: 20px;
    transition: 0.2s background-color;
    cursor: pointer;
}
#box.waiting {
    background-color: gray;
    cursor: move;
}
#box.dragX {
    background-color: red;
    cursor: ew-resize;
}
#box.dragY {
    background-color: green;
    cursor: ns-resize;
}
#box.press {
    background-color: yellow;
    cursor: progress;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/2.5.3/rx.all.js"></script>
<ol>
    <li>Drag horizontally</li>
    <li>Release</li>
    <li>Drag vertically</li>
    <li>Relase</li>
    <li>Press and hold</li>
</ol>
<div id="box"></div>