简体   繁体   中英

D3 force directed graph drag and zoom functions not working

I've been trying to implement a D3 force directed graph function to give a visual representation to my data, So far I've been successful in getting the nodes to display on the screen and have the individual nodes draggable, however I have been unsuccessful in getting the whole network to pan and zoom.

I've looked at countless examples online, but haven't really been able to figure out what I'm doing wrong.

Could someone point me in the right direction please, using d3 version 4

function selectableForceDirectedGraph(){

var width = d3.select('svg').attr('width');
var height = d3.select('svg').attr('height');

var color = d3.scaleOrdinal(d3.schemeCategory20);

var svg = d3.select("svg")
.attr('width',width)
.attr('height',height);

var container = svg.append("g")
.on("zoom",zoomed)
.on("start",dragstarted)
.on("drag",dragged)
.on("end",dragended);

var json_nodes = _dict['one']['nodes'];
var json_links = _dict['one']['links'];

var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(function(d){return d.id}))
    .force("charge", d3.forceManyBody())
    .force("center", d3.forceCenter(width / 2, height / 2));

  var link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(json_links)
    .enter().append("line")
    .attr("stroke-width", function(d) { return Math.sqrt(d.value); })
    .style("marker-end","url(#suit)");

  var node = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(json_nodes)
    .enter().append("circle")
    .attr("r", 5)
    .attr("fill", function(d) { return color(d.group); })
    .call(d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended));

  node.append("title")
      .text(function(d) { return d.id; });

  simulation
      .nodes(json_nodes)
      .on("tick", ticked);

  simulation.force("link")
      .links(json_links);

  function ticked() {
    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("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; });
  }

function dragstarted(d) {
  if (!d3.event.active) simulation.alphaTarget(0.3).restart();
  d.fx = d.x;
  d.fy = d.y;
}

function dragged(d) {
  d.fx = d3.event.x;
  d.fy = d3.event.y;
}

function dragended(d) {
  if (!d3.event.active) simulation.alphaTarget(0);
  d.fx = null;
  d.fy = null;
}

//Zoom functions 
function zoomed(){
    container.attr("transform","translate(" + d3.event.translate + ")scale(" +d3.event.scale + ")");
}
}

The HTML doc that this function is called from has an SVG instantiated (hence why the SVG is selected, not appended like in most other examples)

The two variables "json_nodes" and "json_links" take formatted nodes and links (like what you'd see in a JSON file) from a text file and passes them as the data (wanted to do this offline so when I go overseas). The format for the data is like so:

nodes:[{"id": "Name", "group": integer},...],

links:[{"source": "Name", "target": "Name", "value": integer},...]

I apologise if this is a repeated question, I haven't been able to find any really intuitive help with this.

After some intense staring at the screen for a couple hours, I realised a number of little housekeeping tips to make mine, and hopefully to any other programmers new to D3, applications easier to understand and less prone to errors.

As it turns out from my last example, I was trying to perform zoom functions when the zoom variable containing the D3 .zoom() method hadn't been added to the links variable (as I have pointed out below). Once I had done this, everything worked perfectly.

I've also added some comments as to make the improve the readability of the code from the original question, these changes make it easier to understand and much easier to build upon (much like the inheritance with python classes that I'm familiar with).

So hopefully my little moment of frustration is useful to someone in the future, until the next question, happy debugging :)

Mr Incompetent.

function selectableForceDirectedGraph(){

var width = d3.select('svg').attr('width');
var height = d3.select('svg').attr('height');

var color = d3.scaleOrdinal(d3.schemeCategory20);

//As the height and width have already been set, no need to reset them.
var svg = d3.select("svg");

//This is the container group for the zoom
var container = svg.append("g")
.attr("class","everything");

//see the above question for explanation for purpose of these variable.
var json_nodes = _dict['one']['nodes'];
var json_links = _dict['one']['links'];

var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(function(d){return d.id}))
    .force("charge", d3.forceManyBody())
    .force("center", d3.forceCenter(width / 2, height / 2));

//drawing lines for the links
  var link = container.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(json_links)
    .enter().append("line")
    .attr("stroke-width", function(d) { return Math.sqrt(d.value); })
    .style("marker-end","url(#suit)");

//draw the circles for the nodes
  var node = container.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(json_nodes)
    .enter().append("circle")
    .attr("r", 5)
    .attr("fill", function(d) { return color(d.group); });

//HOUSE KEEPING NOTE: add handlers for drag and zoom as to prevent DRY
var drag_controls = d3.drag()
  .on("start",dragstarted)
  .on("drag",dragged)
  .on("end",dragended);

drag_controls(node); //adding the drag event handlers to the nodes

var zoom_controls = d3.zoom()
.on("zoom",zoomed);

zoom_controls(svg); //adding the zoom event handler to the svg container

  node.append("title")
      .text(function(d) { return d.id; });

  simulation
      .nodes(json_nodes)
      .on("tick", ticked);

  simulation.force("link")
      .links(json_links);

  function ticked() {
    link //updates the link positions
        .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 //update the node positions
        .attr("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; });
  }

function dragstarted(d) {
  if (!d3.event.active) simulation.alphaTarget(0.3).restart();
  d.fx = d.x;
  d.fy = d.y;
}

function dragged(d) {
  d.fx = d3.event.x;
  d.fy = d3.event.y;
}

function dragended(d) {
  if (!d3.event.active) simulation.alphaTarget(0);
  d.fx = null;
  d.fy = null;
}

//Zoom functions 
function zoomed(){
    container.attr("transform",d3.event.transform)
}
}

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