web-dev-qa-db-fra.com

Existe-t-il un moyen de zoomer sur un graphique de disposition de force D3?

D3 a une disposition forcée ici . Existe-t-il un moyen d'ajouter un zoom à ce graphique? Actuellement, j'ai pu capturer l'événement de la molette de la souris, mais je ne sais pas vraiment comment écrire la fonction de redessinage elle-même. Aucune suggestion?

    var vis = d3.select("#graph")
        .append("svg:svg")
        .call(d3.behavior.zoom().on("zoom", redraw)) // <-- redraw function
        .attr("width", w)
        .attr("height", h);
80
Legend

Mise à jour 6/4/14

Voir aussi réponse de Mike Bostock ici pour les changements dans D3 v.3 et le exemple connexe . Je pense que cela remplace probablement la réponse ci-dessous.

Mise à jour 18/02/2014

Je pense que la réponse de @ ahaarnos est préférable si vous voulez que l'ensemble du SVG soit panoramique et zoom. Les éléments imbriqués g dans ma réponse ci-dessous ne sont vraiment nécessaires que si vous avez des éléments sans zoom dans le même SVG (pas le cas dans la question d'origine). Si vous faites appliquez le comportement à un élément g, un arrière-plan rect ou un élément similaire est requis pour garantir que le g reçoit événements de pointeur.

Réponse originale

J'ai obtenu ce travail basé sur l'exemple zoom-pan-transform - vous pouvez voir mon jsFiddle ici: http://jsfiddle.net/nrabinowitz/QMKm3/

C'était un peu plus complexe que je l'espérais - vous devez imbriquer plusieurs éléments g pour le faire fonctionner, définir l'attribut pointer-events Du SVG sur all, puis ajouter un rectangle d'arrière-plan pour recevoir les événements du pointeur (sinon cela ne fonctionne que lorsque le pointeur se trouve sur un nœud ou un lien). La fonction redraw est relativement simple, il suffit de définir une transformation sur le g le plus intérieur:

var vis = d3.select("#chart")
  .append("svg:svg")
    .attr("width", w)
    .attr("height", h)
    .attr("pointer-events", "all")
  .append('svg:g')
    .call(d3.behavior.zoom().on("zoom", redraw))
  .append('svg:g');

vis.append('svg:rect')
    .attr('width', w)
    .attr('height', h)
    .attr('fill', 'white');

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

Cela redimensionne efficacement l'ensemble du SVG, de sorte qu'il redimensionne également la largeur du trait, comme un zoom avant sur une image.

Il existe un autre exemple qui illustre une technique similaire.

97
nrabinowitz

Pourquoi les <g> Imbriqués?

Ce code ci-dessous a bien fonctionné pour moi (un seul <g>, Sans grand blanc aléatoire <rect>:

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 + ")");
}

Où tous les éléments de votre svg sont ensuite ajoutés à l'élément vis.

18
aaaronic

Les réponses fournies fonctionnent dans D3 v2 mais pas dans v3. J'ai synthétisé les réponses dans une solution propre et résolu le problème v3 en utilisant la réponse fournie ici: Pourquoi d3.js v3 casse-t-il mon graphique de force lors de l'implémentation du zoom alors que v2 ne fonctionne pas?

D'abord le code principal. Ceci est une version nettoyée de la réponse de @ahaarnos:

    var svg = d3.select("body")
        .append("svg")
        .attr("width", width)
        .attr("height", height)
            .call(d3.behavior.zoom().on("zoom", redraw))
        .append('g');

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

Vous disposez maintenant d'un panoramique et d'un zoom, mais vous ne pourrez pas faire glisser les nœuds car la fonctionnalité de panoramique remplacera la fonctionnalité de glisser. Nous devons donc le faire:

var drag = force.stop().drag()
.on("dragstart", function(d) {
    d3.event.sourceEvent.stopPropagation(); // to prevent pan functionality from 
                                            //overriding node drag functionality.
    // put any other 'dragstart' actions here
});

Voici le violon de @nrabinowitz modifié pour utiliser cette implémentation de zoom plus propre, mais illustrant comment D3v3 brise la traînée des nœuds: http://jsfiddle.net/QMKm3/718/

Et voici le même violon modifié pour fonctionner avec D3v3: http://jsfiddle.net/QMKm3/719/

14
David Marx

J'ai réussi à faire fonctionner mon graphe sans la seconde annexe "svg: g".

[...].attr("pointer-events", "all")
     .attr("width", width2)
     .attr("height", height2)
     .append('svg:g')
     .call(d3.behavior.zoom().on("zoom", redraw));

Le reste est le même.

2
cem

Si vous souhaitez zoomer et déplacer la mise en page sans changer la taille du nœud, essayez ci-dessous. Vous pouvez également faire glisser les nœuds sans trembler. Ce code est basé sur l'exemple de disposition de force d'origine. En ce qui concerne les données des nœuds et des liens, veuillez vous référer aux exemples de données d'origine. http://bl.ocks.org/mbostock/4062045

Veuillez noter les variables xScale et yScale, les fonctions dragstarted (), dragged () et dragended (). La fonction tick () a également été modifiée.

Vous pouvez voir le résultat sur http://steelblue.tistory.com/9 La langue du site est coréenne. Cependant, vous pouvez facilement trouver le résultat dans le troisième exemple de la page.

var graph = {
    "nodes": [
      { "name": "Myriel", "group": 1 },
      { "name": "Napoleon", "group": 1 },
      // ......
      { "name": "Mme.Hucheloup", "group": 8 }
    ],
    "links": [
      { "source": 1, "target": 0, "value": 1 },
      { "source": 2, "target": 0, "value": 8 },
    // .......
      { "source": 76, "target": 58, "value": 1 }
    ]
};
var width = 640,
    height = 400;
 var color = d3.scale.category20();



var xScale = d3.scale.linear()
        .domain([0, width])
         .range([0, width]);

var yScale = d3.scale.linear()
    .domain([0, height])
   .range([0, height]);
var zoomer = d3.behavior.zoom().x(xScale).y(yScale).scaleExtent([0.1, 8]).on("zoom", zoom);
function zoom() {

    tick(); 
};

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

function dragstarted(d) {
    d3.event.sourceEvent.stopPropagation();

    d.fixed |= 2;         
}
function dragged(d) {

    var mouse = d3.mouse(svg.node());
    d.x = xScale.invert(mouse[0]);
    d.y = yScale.invert(mouse[1]);
    d.px = d.x;         
    d.py = d.y;
    force.resume();
}

function dragended(d) {

    d.fixed &= ~6;           }

var force = d3.layout.force()
    .charge(-120)
    .linkDistance(30)
    .size([width, height]);

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

svg.call(zoomer);

    force
        .nodes(graph.nodes)
        .links(graph.links)
        .start();

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

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

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

    force.on("tick",tick);

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

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


    };
0
seungbum kim

J'ai obtenu une solution pour le graphique D3 à force dirigée avec option de zoom.

    var m = [40, 240, 40, 240],
    width = 960,
    height = 700,
    root;
var svg = d3.select("body").append("svg")
    .attr("class", "svg_container")
    .attr("width", width)
    .attr("height", height)
    .style("overflow", "scroll")
    .style("background-color", "#EEEEEE")
    .append("svg:g")
    .attr("class", "drawarea")
    .append("svg:g")
    .attr("transform", "translate(" + m[3] + "," + m[0] + ")");

//applying zoom in&out for svg
d3.select("svg") 
.call(d3.behavior.zoom()
    .scaleExtent([0.5, 5])
    .on("zoom", zoom));

//zooming 
function zoom() { //zoom in&out function 
    var scale = d3.event.scale,
        translation = d3.event.translate,
        tbound = -height * scale,
        bbound = height * scale,
        lbound = (-width + m[1]) * scale,
        rbound = (width - m[3]) * scale;
    // limit translation to thresholds
    translation = [
        Math.max(Math.min(translation[0], rbound), lbound),
        Math.max(Math.min(translation[1], bbound), tbound)
    ];
    d3.select(".drawarea")
        .attr("transform", "translate(" + translation + ")" +
            " scale(" + scale + ")");
}
0