Comment faire un graphique de mise en page de force dans D3.js sensible à la taille de l'écran / navigateur

J'ai un graphique utilisant la disposition de force, mais il a une largeur fixe w et une hauteur h:

var svg = d3.select("#viz").append("svg")
            .attr("id", "playgraph")
            .attr("width", w)
            .attr("height", h)

var force = d3.layout.force()
              .nodes(nodes)
              .links(links)
              .charge(-1600)
              .linkDistance(45)
              .size([w, h]); 

Qui se traduit par un graphique svg qui ne s'adapte pas ou ne diminue pas malgré les changements dans la taille de l'écran ou de la fenêtre du navigateur. Afin de le rendre réactif (c'est-à-dire se redimensionne automatiquement), j'ai essayé d'utiliser les attributs viewBox et preserveAspectRatio:

var svg = d3.select("#viz").append("svg")
            .attr("id", "playgraph")
            .attr("width", w)
            .attr("height", h)
            .attr("viewBox", "0, 0, 600, 400")
            .attr("preserveAspectRatio", "xMidYMid meet");

Malheureusement, cela n'a pas fonctionné car rien ne se passe lorsque j'ajuste la taille de la fenêtre du navigateur. Je me demande si le .size([w, h]) du Graphe de force a quelque chose à faire avec cela.

Veuillez apporter un peu de lumière sur l'utilisation des attributs viewBox et preserveAspectRatio avec des graphiques de disposition de force.

48
demandé sur VividD 2012-08-14 01:20:22

3 réponses

Le problème n'est pas dans .size(), c'est que vous indiquez les dimensions SVG dans .attr("width", w) .attr("height", h). Supprimez ces deux attributs et vous obtiendrez le droit...

var svg = d3.select("#viz").append("svg")
            .attr("id", "playgraph")
             //better to keep the viewBox dimensions with variables
            .attr("viewBox", "0 0 " + w + " " + h )
            .attr("preserveAspectRatio", "xMidYMid meet");

Http://jsfiddle.net/aaSjd/

52
répondu Duopixel 2012-08-14 09:01:27

La solution ici: http://bl.ocks.org/mbostock/3355967 a bien fonctionné pour moi!

window.addEventListener('resize', resize); 

function resize() {
    var width = window.innerWidth, height = window.innerHeight;
    svg.attr("width", width).attr("height", height);
    force.size([width, height]).resume();
}

Assurez-vous d'exécuter resize() après avoir ajouté toutes vos lignes, nœuds, etc.

9
répondu Matt Jensen 2017-01-04 16:38:47

Duopixel est très proche de ce dont j'avais besoin, sauf que je ne sais pas pourquoi il a imbriqué deux éléments <g> et attaché les écouteurs d'événements au <g> le plus externe (nécessitant également un rectangle invisible derrière tout pour que le g réagisse aux événements sur tout son espace).

Il est plus facile d'attacher les écouteurs au <svg> lui-même et vous n'avez besoin que d'un <g> interne.

Voici mon exemple de force en plein écran:

var width = 1000,
    height = 1000;

var color = d3.scale.category20();

var svg = d3.select("body")
    .append("svg")
      .attr({
        "width": "100%",
        "height": "100%"
      })
      .attr("viewBox", "0 0 " + width + " " + height )
      .attr("preserveAspectRatio", "xMidYMid meet")
      .attr("pointer-events", "all")
    .call(d3.behavior.zoom().on("zoom", redraw));

var vis = svg
    .append('svg:g');

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

function draw_graph(graph) {
  var force = d3.layout.force()
      .charge(-120)
      .linkDistance(30)
      .nodes(graph.nodes)
      .links(graph.links)
      .size([width, height])
      .start();

  var link = vis.selectAll(".link")
      .data(graph.links)
      .enter().append("line")
      .attr("class", "link")
      .style("stroke-width", function(d) { return Math.sqrt(d.value); });

  var node = vis.selectAll(".node")
      .data(graph.nodes)
      .enter().append("circle")
      .attr("class", "node")
      .attr("r", 5)
      .style("fill", function(d) { return color(d.group); })
      .call(force.drag);

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

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

draw_graph(data);
4
répondu aaaronic 2013-03-07 20:29:00