javascriptsvgd3.jsdragforce-layout

d3.js: Drag is disabled when use zoom with force layout


I have seen this question: Is there a way to zoom into a D3 force layout graph?

But I got some unexpected behaivor from my graph - after few drags or zoom or pan all nodes just freeze and drag stop working.

I created this fiddle: http://jsfiddle.net/7gpweae9/9/

The main part of the code:

var svg = d3.select("#graph")
    .append("svg:svg")
        .attr("width", width)
        .attr("height", height)
        .attr("pointer-event", "all")
    .append("svg:g")
        .call(d3.behavior.zoom().on("zoom", zoom))
    .append("svg:g");

svg.append("svg:rect")
    .attr("width", width)
    .attr("height", height)
    .attr('fill', 'white');

var link = svg.selectAll(".link");
var node = svg.selectAll(".node");

var force = d3.layout.force()
    .nodes(nodes)
    .links(links)
    .size([width,height])
    .linkDistance(100)
    .charge(-400)
    .start();

var drag = d3.behavior.drag()
    .origin(function(d) { return d; })
    .on("dragstart", dragstarted)
    .on("drag", dragged)
    .on("dragend", dragended);

node = svg.selectAll(".node")
    .data(nodes)
    .enter().append("g")
    .attr("class", "node")
    .call(drag);

node.append("circle")
    .attr("class", "node-circle")
    .attr("r", 12);

node.append("text")
    .attr("x", 12)
    .attr("y", ".35em")
    .text(function(d) { return d.word; });

link = svg.selectAll(".link")
    .data(links)
    .enter().append("line")
    .attr("class", "link");

force.on("tick", function() {
    link.attr("x1", function(d) { return d.source.x; })
        .attr("y1", function(d) { return d.source.y; })
        .attr("x2", function(d) { return d.target.x; })
        .attr("y2", function(d) { return d.target.y; });

    node.attr("transform", function(d) {
        return "translate(" + d.x + "," + d.y + ")";
    });
});

function zoom() {
    svg.attr("transform",
        "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}

function dragstarted(d) {
    d3.event.sourceEvent.stopPropagation();
    d3.select(this).classed("dragging", true);
}

function dragged(d) {
    d3.select(this).attr("x", d.x = d3.event.x).attr("y", d.y = d3.event.y);
}

function dragended(d) {
    d3.select(this).classed("dragging", false);
}

Perhaps I missed something, I've never used d3 before.

UPD: It seems that freezing occurs after a certain period of time.


Solution

  • I replaced d3.layout.force() to force.drag() and now it works almost fine.

        var nodes;
        var links;
        prepareData();
    
        var graph = document.querySelectorAll("#graph")[0];
        var height = 500;
        var width = 500;
    
        var svg = d3.select("#graph").append("svg:svg")
                .attr("width", width)
                .attr("height", height)
                .attr("pointer-event", "all")
            .append("svg:g")
                .call(d3.behavior.zoom().on("zoom", zoom))
            .append("svg:g");
    
        svg.append("svg:rect")
            .attr("width", width)
            .attr("height", height)
            .attr('fill', 'white');
    
    
    
        var link = svg.selectAll(".link");
        var node = svg.selectAll(".node");
    
        var force = d3.layout.force()
            .nodes(nodes)
            .links(links)
            .size([width,height])
            .linkDistance(100)
            .charge(-400)
            .start();
    
        var drag = force.drag()
            .origin(function(d) { return d; })
            .on("dragstart", dragstarted)
            .on("drag", dragged)
    
        node = svg.selectAll(".node")
            .data(nodes)
            .enter().append("g")
            .attr("class", "node")
            .call(drag);
    
        node.append("circle")
            .attr("class", "node-circle")
            .attr("r", 12);
    
        node.append("text")
            .attr("x", 12)
            .attr("y", ".35em")
            .text(function(d) { return d.word; });
    
        link = svg.selectAll(".link")
            .data(links)
            .enter().append("line")
            .attr("class", "link");
    
        force.on("tick", function() {
            link.attr("x1", function(d) { return d.source.x; })
                .attr("y1", function(d) { return d.source.y; })
                .attr("x2", function(d) { return d.target.x; })
                .attr("y2", function(d) { return d.target.y; });
    
            node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
        });
    
        function zoom() {
            svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
        }
    
        function dragstarted(d) {
            d3.event.sourceEvent.stopPropagation();
        }
    
        function dragged(d) {
            d3.select(this).attr("x", d.x = d3.event.x).attr("y", d.y = d3.event.y);
        }
    
        function prepareData() {
            nodes = [{"index":0,"word":"edit"},{"index":1,"word":"course","sentences":[29859]},{"index":2,"word":"needs","sentences":[29859]},{"index":3,"word":"fit","sentences":[29859]},{"index":4,"word":"slides","sentences":[29859]},{"index":5,"word":"print","sentences":[29859]},{"index":6,"word":"can","sentences":[29859]}];
                    links = [{"source":0,"target":1},{"source":0,"target":2},{"source":0,"target":3},{"source":0,"target":4},{"source":0,"target":5},{"source":0,"target":6}]
        }
    svg {
        border: 1px solid black;
    }
    
    .link {
        stroke: #000;
        stroke-width: 1px;
    }
    
    .node-circle {
        cursor: move;
        fill: #ccc;
        stroke: #000;
        stroke-width: 2px;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
    <body>
        <div id="graph"></div>
    </body>