I'm experimenting with drag-and-drop using cyclejs in a codepen. The standard drag methods supported by HTML 5 don't seem to support constraints on the movement of the dragged object so I went with standard mousedown/mousemove/mouseup. It works, but not consistently. The combine() operation doesn't seem to trigger even when the debug() calls show that mousedown and mousemove events have been received and sometimes the mouseup is missed. Perhaps my understanding of the operation is incomplete or incorrect. A direct link to the codepen is provided at the bottom of this post. Any help appreciated!
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")
});
Testing your code shows that
combine
is not getting the firstmousedown
event, apparently due to thebetween
operator subscribing tomousedown$
after the first mousedown event. Addingremember
to themousedown$
sends that first mousedown event to the between operator subscription.Codepen.io
remember
example.Codesandbox.io testing
between
Here's another CycleJS/xstream Drag and Drop approach (taking inspiration from this RxJS Drag and Drop example) I think is more straight forward. Everything else in your code is essentially the same but the
model
function is this:Here's a Codepen.io example.