Search code examples
javascriptdrag-and-droptouchonkeyupbacon.js

Bacon.js: select, drag-drop with mouse and touch and move using arrow keys


We are writing an application like powerpoint, and we're considering bacon.js. There are elements like pictures, text, etc that can be dragged onto the screen, then selected, moved using mouse drag drop or touch events and using the arrow keys.

I made this simple sample using some guidelines from before with 2 divs that can be dragged around using the mouse: http://jsfiddle.net/5y6qqqg2/3/

// allKeyUps :: Observable KeyEvent
var keyDown = $(document)
.asEventStream('keyDown')
.filter(function(ev){
    return ev.keyCode==37 || 
        ev.keyCode==38 ||
        ev.keyCode==39 ||
        ev.keyCode==40
}).log()






function xyFromEvent(v) {
    return {
        x: v.clientX,
        y: v.clientY
    }
}

function getDelta(t) {
    var a = t[1];
    var b = t[0];
    return {
        x: a.x - b.x,
        y: a.y - b.y
    };
}

function add(p1, p2) {
    return {
        x: p1.x + p2.x,
        y: p1.y + p2.y
    };
}

$().ready(function () {
    var onMove = $("html").asEventStream('mousemove')
    addDraggable($("#1"), onMove);
    addDraggable($("#2"), onMove);
});

function addDraggable(block, onMove) {
    var startDrag = block.asEventStream('mousedown')
    var endDrag = block.asEventStream('mouseup')


    var draggingDeltas = startDrag.flatMap(function () {
        return onMove.map(xyFromEvent)
            .slidingWindow(2, 2)
            .map(getDelta)
            .takeUntil(endDrag)
    })



    var blockPosition = draggingDeltas.scan({
        x: 0,
        y: 0
    }, add);

    blockPosition.onValue(function (pos) {
        block.css({
            top: pos.y + "px",
            left: pos.x + "px"
        });
    });
}

but I am not experienced enough with bacon.js to be able to hook the eventstream for keydown to move only one element not all of them.


Solution

  • I would do something like this:

    • For each movable element, create a selected stream (from clicks I'm guessing) and map each click to the id of the element.
    • Merge all those streams to create a property of the currently selected element.
    • Map the keyDown stream to deltas, with up being {x: 0, y: -1}, let's call it e.g. keyDelta
    • Combine the selectedElement and keyDelta streams to create a stream of move events, e.g. {id: 37, delta: {x: 0, y: -1}, let's call it keyDeltas
    • Refactor you dragging code to produce a stream with same kind of values (id & delta)
    • Merge the keyDeltas and dragging streams, now you have a single stream with all moves of all objects
    • Scan that stream, now you a stream with the position of all objects
    • onValue of the position stream, do the rendering.