d3 combine zoom and drag calls?

Viewed 235

Is it possible to conditionally ignore the drag function from eating an event? I have a pan/zoom canvas (as seen here: www.proofapp.io/workspace) and I'm trying to implement a shift+drag multi-selection lasso. The svg object already has the zoom function registered so when I put a drag function above it the zoom never gets called. Since call only runs once at the beginning I'm not sure how I can make this work. Any suggestions?

const zoom = d3.zoom()
                    .scaleExtent([0.25 ,5])
                    .on("zoom", function() {
                        root.attr('transform', d3.event.transform);
                    });

// THIS DOESNT WORK BECAUSE IT ONLY RUNS ONCE AT THE BEGINNING
const lasso = function() {
    if (d3.event.sourceEvent) {
        if (d3.event.sourceEvent.shiftKey) {
            d3.drag()
                .dragDisable() // maybe the answer is with this?
                .on("start", function() { console.log('lasso-start') })
                .on("drag", function() { console.log('lasso-drag') })
                .on("end", function() { console.log('lasso-end') });
        }
    }
}

var svg = d3.select("div#nodegraph")
                    .append("svg")
                        .attr("width", "100%")
                        .attr("height", "100%")
                        .on('click', Graph.setNodeInactive)
                        .call(lasso)
                        .call(zoom);

UPDATE

Trying to just use the mousedown.drag event instead so that I can control the event bubbling. Not quite there yet, but the behaviour is correct (only blocks zoom when shift is pressed).

const zoom = d3.zoom()
                    .scaleExtent([0.25 ,5])
                    .on("zoom", function() {
                        root.attr('transform', d3.event.transform);
                    });

function lasso() {
    if (d3.event.shiftKey) {
        // do stuff
        d3.event.stopImmediatePropagation();
    }
}

var svg = d3.select("div#nodegraph")
                    .append("svg")
                        .attr("width", "100%")
                        .attr("height", "100%")
                        .on('click', Graph.setNodeInactive)
                        .on('mousedown.drag', lasso)
                        .call(zoom);
1 Answers

If there's a d3 way to do this I'd still love to know how it works to take advantage of all the extra goodness d3.drag does. Until that happens, here is a fairly complete example that takes into account the zoomed canvas.

Selection = {};
Selection.DragLasso = {};
Selection.DragLasso.__lasso = null;
Selection.DragLasso.handler = function() {
    if (d3.event.shiftKey) {
        d3.event.stopImmediatePropagation();

        if (Selection.DragLasso.__lasso) {
            Selection.DragLasso.__lasso.remove();
            Selection.DragLasso.__lasso = null;
        }

        var m = d3.mouse(this);

        svg
            .on('mousemove.drag', Selection.DragLasso.drag)
            .on('mouseup.drag', Selection.DragLasso.end);

        var z = d3.zoomTransform(svg.node());
        var x = (z.x / z.k * -1) + (m[0] / z.k);
        var y = (z.y / z.k * -1) + (m[1] / z.k);

        Selection.DragLasso.__lasso = noderoot.append('rect')
                                                    .attr("fill", 'red')
                                                    .attr('x', x)
                                                    .attr('y', y)
                                                    .attr('width', 0)
                                                    .attr('height', 0)
                                                    .classed('selection-lasso', true);
    }
}
Selection.DragLasso.drag = function(e) {
    var m = d3.mouse(this);

    var z = d3.zoomTransform(svg.node());
    var x = (z.x / z.k * -1) + (m[0] / z.k);
    var y = (z.y / z.k * -1) + (m[1] / z.k);

    Selection.DragLasso.__lasso
        .attr("width", Math.max(0,  x - +Selection.DragLasso.__lasso.attr("x")))
        .attr("height", Math.max(0, y - +Selection.DragLasso.__lasso.attr("y")));

}
Selection.DragLasso.end = function() {
    svg.on('mousemove.drag', null).on('mouseup.drag', null);
    Selection.DragLasso.__lasso.remove();
    Selection.DragLasso.__lasso = null;
}


var svg = d3.select("div#nodegraph")
                    .append("svg")
                        .attr("width", "100%")
                        .attr("height", "100%")
                        .on('mousedown.drag', Selection.DragLasso.handler)
                        .call(zoom);
Related