javascript - Ändra storlek på svg när fönstret ändras i d3.js


Jag ritar ett scatterplot med d3.js. Med hjälp av denna fråga:
Få storleken på skärmen, nuvarande webbsida och webbläsarfönster

Jag använder det här svaret:

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;

Så jag kan anpassa mitt diagram till användarens fönster så här:

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

Nu skulle jag vilja att något tar hand om att ändra storlek på diagrammet när användaren ändrar storlek på fönstret.

PS: Jag använder inte jQuery i min kod.

Svar #1

Sök efter 'responsiv SVG', det är ganska enkelt att göra en SVG responsiv och du behöver inte längre oroa dig för storlekar.

Här är hur jag gjorde det:

d3.select("div#chartId")
   .append("div")
   // Container class to make it responsive.
   .classed("svg-container", true) 
   .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)
   // Fill with a rectangle for visualization.
   .append("rect")
   .classed("rect", true)
   .attr("width", 600)
   .attr("height", 400);
.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;
}

svg .rect {
  fill: gold;
  stroke: steelblue;
  stroke-width: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

<div id="chartId"></div>

Observera: Allt i SVG-bilden kommer att skalas med fönstrets bredd. Detta inkluderar streckbredd och teckenstorlekar (även de som är inställda med CSS). Om detta inte är önskvärt finns det mer omfattande alternativa lösningar nedan.

Mer info/handledningar:

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

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

Svar #2

Använd 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/

Svar #3

UPPDATERING använd bara det nya sättet från @cminatti

gammalt svar för historiska ändamål

Enligt min åsikt är det bättre att använda select() och on() eftersom du på det sättet kan ha flera resize-händelsehanterare... bara se till att inte bli för galen

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/

Svar #4

Det är ganska fult om kod för att ändra storlek är nästan lika lång som koden för att bygga diagrammet från början. Så i stället för att ändra storlek på varje element i det befintliga diagrammet, varför inte helt enkelt ladda om det? Så här fungerade det för mig:

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...
}

Den avgörande raden är d3.select('svg').remove();. Annars kommer varje storleksändring att lägga till ett nytt SVG-element under det föregående.

Relaterade artiklar