2012-08-13 10 views
45

Mam wykres stosując układ sił, ale ma stałą szerokość i wysokość hw:Jak zrobić wykres układu sił w D3.js reagującego na wielkość ekranu/przeglądarki

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]); 

co skutkuje SVG wykres, który nie zmienia się ani nie zmniejsza pomimo zmian rozmiaru okna lub okna przeglądarki. W celu uczynienia go reaguje (tzn automatycznie zmienia rozmiar się), próbowałem za pomocą viewBox i preserveAspectRatio atrybuty:

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

Niestety, to nie działa jak nic się nie dzieje, kiedy dostosować rozmiar okna przeglądarki. Zastanawiam się, czy .size([w, h]) wykresu siły ma coś z tym wspólnego.

Proszę rzucić trochę światła na sposób korzystania z atrybutów viewBox i preserveAspectRatio z wykresami układu sił.

+0

Możesz także dołączyć funkcję przerysowywania do 'window.onresize' – Wex

+0

To pytanie jest dość stare, ale dla tych, którzy uważają to za przydatne, możesz również rozważyć ograniczenie również tych węzłów do ograniczonego pola. Podczas zmiany rozmiaru węzły pozostaną na ekranie. Przykład: http://bl.ocks.org/mbostock/1129492 – joshfindit

Odpowiedz

50

Problem nie mieści się w zakresie .size(), tylko że określasz wymiary SVG w .attr("width", w) .attr("height", h). Usuń te dwa atrybuty, a dostaniesz to dobrze ...

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/

+0

dzięki! Teraz rozmiar svg zmienia się. Jednak bez ustawienia "attr (" width ", ...)' i 'attr (" height ", ...)', szerokość/wysokość obrazu wydaje się być szerokość/wysokość bieżącego okna przeglądarki. Czy istnieje sposób, aby ustawić te dwa wymiary elementu 'svg' w innym miejscu, bez wpływu na atrybuty' viewBox' i 'preserveAspectRatio'? – MLister

+3

' 'powinien zacząć. – Duopixel

4

Duopixel jest bardzo zbliżony do tego, co potrzebne, z wyjątkiem nie wiem dlaczego on zagnieżdżone dwa <g> elementów i załączeniu detektory zdarzeń do najbardziej oddalonego <g> (wymagającego niewidocznego prostokąta za wszystkim, aby g reagował na zdarzenia na całej jego przestrzeni).

Łatwiej jest dołączyć słuchaczy do samego siebie, <svg>, a następnie potrzebujesz tylko jednego wewnętrznego <g>.

Oto moja pełnoekranowy siły skierowane przykład:

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); 
8

rozwiązania pokazanego tutaj: http://bl.ocks.org/mbostock/3355967 pracował dobrze dla mnie!

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(); 
} 

Upewnij się, że uruchomiłeś resize() po dodaniu wszystkich linii, węzłów itp.