wake-up-neo.net

Ändern Sie die Größe von SVG, wenn die Fenstergröße in d3.js geändert wird

Ich zeichne mit d3.js ein Streudiagramm. Mit Hilfe dieser Frage:
Liefert die Größe des Bildschirms, die aktuelle Webseite und das Browserfenster

Ich verwende diese Antwort:

var w = window,
    d = document,
    e = d.documentElement,
    g = d.getElementsByTagName('body')[0],
    x = w.innerWidth || e.clientWidth || g.clientWidth,
    y = w.innerHeight|| e.clientHeight|| g.clientHeight;

So kann ich mein Plot wie folgt an das Fenster des Benutzers anpassen:

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

Nun möchte ich, dass sich die Größe der Grafik ändert, wenn der Benutzer die Größe des Fensters ändert.

PS: Ich verwende nicht jQuery in meinem Code.

164
mthpvg

Suchen Sie nach 'responsive SVG'. Es ist ziemlich einfach, eine SVG-Funktion zu aktivieren, und Sie müssen sich nicht mehr um die Größe kümmern. 

So habe ich es gemacht: 

d3.select("div#chartId")
   .append("div")
   .classed("svg-container", true) //container class to make it responsive
   .append("svg")
   //responsive SVG needs these 2 attributes and no width and height attr
   .attr("preserveAspectRatio", "xMinYMin meet")
   .attr("viewBox", "0 0 600 400")
   //class to make it responsive
   .classed("svg-content-responsive", true); 

Der CSS-Code:

.svg-container {
    display: inline-block;
    position: relative;
    width: 100%;
    padding-bottom: 100%; /* aspect ratio */
    vertical-align: top;
    overflow: hidden;
}
.svg-content-responsive {
    display: inline-block;
    position: absolute;
    top: 10px;
    left: 0;
}

Weitere Infos/Tutorials: 

http://thenewcode.com/744/Make-SVG-Responsive

http://soqr.fr/testsvg/embed-svg-liquid-layout-responsive-web-design.php

262
cminatti

Verwenden Sie window.onresize :

function updateWindow(){
    x = w.innerWidth || e.clientWidth || g.clientWidth;
    y = w.innerHeight|| e.clientHeight|| g.clientHeight;

    svg.attr("width", x).attr("height", y);
}
d3.select(window).on('resize.updatesvg', updateWindow);

http://jsfiddle.net/Zb85u/1/

41
Adam Pearce

UPDATE benutze einfach den neuen Weg von @cminatti


alte Antwort für historische Zwecke

IMO ist es besser, select () und on () zu verwenden, da auf diese Weise mehrere Event-Handler für die Größenänderung verwendet werden können

d3.select(window).on('resize', resize); 

function resize() {
    // update width
    width = parseInt(d3.select('#chart').style('width'), 10);
    width = width - margin.left - margin.right;

    // resize the chart
    x.range([0, width]);
    d3.select(chart.node().parentNode)
        .style('height', (y.rangeExtent()[1] + margin.top + margin.bottom) + 'px')
        .style('width', (width + margin.left + margin.right) + 'px');

    chart.selectAll('rect.background')
        .attr('width', width);

    chart.selectAll('rect.percent')
        .attr('width', function(d) { return x(d.percent); });

    // update median ticks
    var median = d3.median(chart.selectAll('.bar').data(), 
        function(d) { return d.percent; });

    chart.selectAll('line.median')
        .attr('x1', x(median))
        .attr('x2', x(median));


    // update axes
    chart.select('.x.axis.top').call(xAxis.orient('top'));
    chart.select('.x.axis.bottom').call(xAxis.orient('bottom'));

}

http://eyeseast.github.io/visible-data/2013/08/28/responsive-charts-with-d3/

32
slf

Es ist ziemlich hässlich, wenn der Code zur Größenänderung fast so lang ist wie der Code zum Erstellen des Diagramms an erster Stelle. Anstatt also jedes Element des vorhandenen Diagramms zu ändern, laden Sie es einfach neu. So funktioniert es für mich:

function data_display(data){
   e = document.getElementById('data-div');
   var w = e.clientWidth;
   // remove old svg if any -- otherwise resizing adds a second one
   d3.select('svg').remove();
   // create canvas
   var svg = d3.select('#data-div').append('svg')
                                   .attr('height', 100)
                                   .attr('width', w);
   // now add lots of beautiful elements to your graph
   // ...
}

data_display(my_data); // call on page load

window.addEventListener('resize', function(event){
    data_display(my_data); // just call it again...
}

Die entscheidende Linie ist d3.select('svg').remove();. Andernfalls wird bei jeder Größenänderung ein weiteres SVG-Element unterhalb des vorherigen hinzugefügt. 

12
Raik

In Force-Layouts können durch einfaches Festlegen der Attribute 'height' und 'width' die Darstellung nicht erneut zentriert oder in den svg-Container verschoben werden. Es gibt jedoch eine sehr einfache Antwort, die für erzwungene Layouts here funktioniert. In Summe: 

Verwenden Sie dasselbe (beliebige) Event, das Sie möchten.

window.on('resize', resize);

Dann nehmen wir an, Sie haben SVG & Force-Variablen: 

var svg = /* D3 Code */;
var force = /* D3 Code */;    

function resize(e){
    // get width/height with container selector (body also works)
    // or use other method of calculating desired values
    var width = $('#myselector').width(); 
    var height = $('#myselector').height(); 

    // set attrs and 'resume' force 
    svg.attr('width', width);
    svg.attr('height', height);
    force.size([width, height]).resume();
}

Auf diese Weise rendern Sie den Graphen nicht vollständig neu, wir setzen die Attribute und d3 berechnet die Dinge nach Bedarf neu. Dies funktioniert zumindest, wenn Sie einen Schwerpunkt verwenden. Ich bin mir nicht sicher, ob dies eine Voraussetzung für diese Lösung ist. Kann jemand das bestätigen oder dementieren? 

Prost, g

8
gavs

Für diejenigen, die kraftgerichtete Graphen in D3 v4/v5 verwenden, ist die size-Methode nicht mehr vorhanden. Etwas wie das folgende funktionierte für mich (basierend auf dieser Github-Ausgabe ):

simulation
    .force("center", d3.forceCenter(width / 2, height / 2))
    .force("x", d3.forceX(width / 2))
    .force("y", d3.forceY(height / 2))
    .alpha(0.1).restart();
0
Justin Lewis

Ich verwende window.innerWidth für die Breite des Clients und window.innerHeight für die Höhe des Clients.

0
Akshay

Wenn Sie eine benutzerdefinierte Logik binden möchten, um die Ereignisgröße zu ändern, können Sie mit today die ResizeObserver-Browser-API für den Begrenzungsrahmen eines SVGElement verwenden.
Dies behandelt auch den Fall, wenn die Größe des Containers aufgrund der Änderung der nahen Elemente geändert wird.
Es gibt eine polyfill für eine breitere Browserunterstützung.

So kann es in der UI-Komponente funktionieren:

<div class="container">
  <svg></svg>
</div>

Komponente:

// Setup observer in constructor
const container = document.querySelector('.container')

const resizeObserver = new ResizeObserver((entries, observer) => {
  // on resize logic specific to this component
})

resizeObserver.observe(container)

// unobserve in component destroy method
this.resizeObserver.disconnect()
0
Anbu Agarwal