如何让 xstream 组合鼠标事件?

how do I get xstream to combine mouse events?

我正在 codepen 中使用 cyclejs 进行拖放试验。 HTML 5 支持的标准拖动方法似乎不支持对拖动对象移动的限制,所以我选择了标准 mousedown/mousemove/mouseup。它有效,但不一致。 combine() 操作似乎不会触发,即使 debug() 调用显示已收到 mousedown 和 mousemove 事件,有时会错过 mouseup。也许我对操作的理解不完整或不正确。此 post 底部提供了代码笔的直接 link。感谢任何帮助!

const xs = xstream.default;
const { run } = Cycle;
const { div, svg, makeDOMDriver } = CycleDOM;

function DragBox(sources) {
  const COMPONENT_NAME = `DragBox`;

  const intent = function({ DOM }) {
    return {
      mousedown$: DOM.select(`#${COMPONENT_NAME}`)
        .events("mousedown")
        .map(function(ev) {
          return ev;
        })
        .debug("mousedown"),
      mousemove$: DOM.select(`#${COMPONENT_NAME}`)
        .events("mousemove")
        .map(function(ev) {
          return ev;
        })
        .debug("mousemove"),
      mouseup$: DOM.select("#container")
        .events("mouseup")
        .map(function(ev) {
          return ev;
        })
        .debug("mouseup")
    };
  };

  const between = (first, second) => {
    return source => first.mapTo(source.endWhen(second)).flatten();
  };

  const model = function({ mousedown$, mousemove$, mouseup$ }) {
    return xs
      .combine(mousedown$, mousemove$)
      .debug("combine")
      .map(([mousedown, mousemove]) => ({
        x: mousemove.pageX - mousedown.layerX,
        y: mousemove.pageY - mousedown.layerY
      }))
      .compose(between(mousedown$, mouseup$))
      .startWith({
        x: 0,
        y: 0
      })
      .debug("model");
  };

  const getStyle = left => top => {
    return {
      style: {
        position: "absolute",
        left: left + "px",
        top: top + "px",
        backgroundColor: "#333",
        cursor: "move"
      }
    };
  };

  const view = function(state$) {
    return state$.map(value =>
      div("#container", { style: { height: "100vh" } }, [
        div(`#${COMPONENT_NAME}`, getStyle(value.x)(value.y), "Move Me!")
      ])
    );
  };

  const actions = intent(sources);
  const state$ = model(actions);
  const vTree$ = view(state$);

  return {
    DOM: vTree$
  };
}

function main(sources) {
  const dragBox = DragBox(sources);

  const sinks = {
    DOM: dragBox.DOM
  };

  return sinks;
}

Cycle.run(main, {
  DOM: makeDOMDriver("#app")
});

https://codepen.io/velociflapter/pen/bvqMGp?editors=1111

Testing your code 显示 combine 没有获得第一个 mousedown 事件,显然是由于 between 运算符在第一次 mousedown 之后订阅了 mousedown$事件。将 remember 添加到 mousedown$ 会将第一个 mousedown 事件发送到 between 运算符订阅。

mousedown$: DOM.select(`#${COMPONENT_NAME}`)
  .events("mousedown").remember()

Codepen.io remember example.

Codesandbox.io testing between


这是另一种 CycleJS/xstream 拖放方法(从这个 RxJS Drag and Drop example 中获得灵感)我认为更直接。您代码中的其他所有内容基本相同,但 model 函数是这样的:

const model = function({ mousedown$, mousemove$, mouseup$ }) {
  return mousedown$.map(md => {
    let startX = md.offsetX, startY = md.offsetY
    return mousemove$.map(mm => {
      mm.preventDefault()
      return {
        x: mm.clientX - startX,
        y: mm.clientY - startY
      }
    }).endWhen(mouseup$)
  }).flatten().startWith({x:0,y:0})
};

这是一个 Codepen.io example