简体   繁体   中英

Implement panning while keeping nodes draggable in d3 force layout

I use the d3.layout.force to visualize graph structures. And I want both node dragging feature and panning feature. (By 'panning', I mean something like google maps behavior.)

As shown in several d3 examples, nodes can be made draggable by nodes.call(force.drag) . And panning can be implemented by d3.behavior.drag .

However, it seems they can not be used simultaneously.

Here is a code example:

var width = 400, height = 300;
var svg = d3.select('body').append('svg').attr({width: width, height: height});

var nodes = [{}, {}, {}, {}];
var links = [
    {source: 1, target: 0},
    {source: 2, target: 0},
    {source: 0, target: 3}
];

var nodeSel = svg.selectAll('circle')
    .data(nodes).enter().append('circle')
    .attr('r', 10).attr('fill', 'black');
var linkSel = svg.selectAll('line').data(links).enter().append('line')
    .attr('stroke', 'black');

var force = d3.layout.force()
    .size([width, height])
    .nodes(nodes).links(links)
    .linkDistance(80)
    .charge(-300)
    .on('tick', function() {
        linkSel
            .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; });
        nodeSel
            .attr('cx', function(d) { return d.x; })
            .attr('cy', function(d) { return d.y; });
    });

nodeSel.call(force.drag);


/* This block of codes spoils force.drag */
/* ****************************************
var drag = d3.behavior.drag();
var viewBoxX = 0, viewBoxY = 0;
drag.on('dragstart', function() {
    console.log('new dragstart is called');
}).on('drag', function() {
    viewBoxX -= d3.event.dx;
    viewBoxY -= d3.event.dy;
    svg.attr('viewBox', [viewBoxX, viewBoxY, width, height].join(' '));
}).on('dragend', function() {
    console.log('new dragend is called');
});
svg.call(drag);
**************************************** */


force.start();

(Working one is on jsfiddle: http://jsfiddle.net/dersinces/hzL8T/1/ .)

This code enables only node dragging, and not panning. Activating a code block at bottom (line 36) enables panning but it makes nodes undraggable.

How can I implement graph panning while keeping nodes draggable?

Here is how you can do it: Demo

The idea is to put the region which had the draggable behavior under the area which contains the nodes so that nodes can still receive mouseevents.

// First append the area where the dragging will happen.
svg.append('rect')
  .classed('bg', true)
  .attr('stroke', 'transparent')
  .attr('fill', 'transparent')
  .attr('x', 0)
  .attr('y', 0)
  .attr('width', width)
  .attr('height', height)
  .call(drag);


// Then add the area which will contain the nodes.
var nodeArea = svg.append('g')
                 .classed('node-area', true);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM