假设我有一个直方图脚本,构建960 500 svg图形。我如何使这个响应,以便调整图形的宽度和高度是动态的?

<script> 

var n = 10000, // number of trials
    m = 10,    // number of random variables
    data = [];

// Generate an Irwin-Hall distribution.
for (var i = 0; i < n; i++) {
  for (var s = 0, j = 0; j < m; j++) {
    s += Math.random();
  }
  data.push(s);
}

var histogram = d3.layout.histogram()
    (data);

var width = 960,
    height = 500;

var x = d3.scale.ordinal()
    .domain(histogram.map(function(d) { return d.x; }))
    .rangeRoundBands([0, width]);

var y = d3.scale.linear()
    .domain([0, d3.max(histogram.map(function(d) { return d.y; }))])
    .range([0, height]);

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

svg.selectAll("rect")
    .data(histogram)
  .enter().append("rect")
    .attr("width", x.rangeBand())
    .attr("x", function(d) { return x(d.x); })
    .attr("y", function(d) { return height - y(d.y); })
    .attr("height", function(d) { return y(d.y); });

svg.append("line")
    .attr("x1", 0)
    .attr("x2", width)
    .attr("y1", height)
    .attr("y2", height);

</script> 

完整的直方图示例要点如下: https://gist.github.com/993912


当前回答

还有另一种不需要重绘图形的方法,它涉及到修改<svg>元素上的viewBox和preserveAspectRatio属性:

<svg id="chart" viewBox="0 0 960 500"
  preserveAspectRatio="xMidYMid meet">
</svg>

15年11月24日更新:大多数现代浏览器可以从viewBox推断SVG元素的纵横比,所以你可能不需要保持图表的大小。如果你需要支持旧的浏览器,你可以在窗口调整大小时调整元素的大小,就像这样:

var aspect = width / height,
    chart = d3.select('#chart');
d3.select(window)
  .on("resize", function() {
    var targetWidth = chart.node().getBoundingClientRect().width;
    chart.attr("width", targetWidth);
    chart.attr("height", targetWidth / aspect);
  });

svg内容将自动缩放。你可以在这里看到一个工作示例(进行了一些修改):调整窗口或右下角窗格的大小,看看它是如何反应的。

其他回答

如果你正在使用d3.js到c3.js的响应性问题的解决方案是相当简单的:

var chart = c3.generate({bindTo:"#chart",...});
chart.resize($("#chart").width(),$("#chart").height());

生成的HTML是这样的:

<div id="chart">
    <svg>...</svg>
</div>

以防人们还在问这个问题——以下是对我有用的方法:

Enclose the iframe in a div and use css to add a padding of, say, 40% to that div (the percentage depending on the aspect ratio you want). Then set both width and height of the iframe itself to 100%. In the html doc containing the chart to be loaded in the iframe, set width to the width of the div that the svg is appended to (or to the width of the body) and set height to width * aspect ratio. Write a function that reloads the iframe content upon window resize, so as to adapt the size of the chart when people rotate their phone.

在我的网站上有一个例子: http://dirkmjk.nl/en/2016/05/embedding-d3js-charts-responsive-website

2016年12月30日更新

我上面描述的方法有一些缺点,特别是它没有考虑任何不在d3创建的svg中的标题和说明文字的高度。后来我想到了一个我认为更好的方法:

将D3图表的宽度设置为它所附的div的宽度,并使用纵横比来相应地设置它的高度; 使用HTML5的postMessage将嵌入页面的高度和url发送到父页面; 在父页面上,使用url识别相应的iframe(如果页面上有多个iframe,则很有用),并将其高度更新为嵌入页面的高度。

例子在我的网站上:http://dirkmjk.nl/en/2016/12/embedding-d3js-charts-responsive-website-better-solution

D3数据连接的基本原则之一是它是幂等的。换句话说,如果重复计算使用相同数据的数据连接,则呈现的输出是相同的。因此,只要你正确地渲染图表,注意你的进入,更新和退出选择-当大小发生变化时,你所要做的就是重新渲染整个图表。

还有一些其他的事情你应该做,一个是取消窗口调整大小处理程序,以抑制它。此外,这应该通过测量包含元素来实现,而不是硬编码宽度/高度。

作为替代方案,下面是使用d3fc呈现的图表,d3fc是一组正确处理数据连接的D3组件。它还有一个笛卡尔图,可以测量它所包含的元素,从而很容易创建“响应性”图表:

// create some test data
var data = d3.range(50).map(function(d) {
  return {
    x: d / 4,
    y: Math.sin(d / 4),
    z: Math.cos(d / 4) * 0.7
  };
});

var yExtent = fc.extentLinear()
  .accessors([
    function(d) { return d.y; },
    function(d) { return d.z; }
  ])
  .pad([0.4, 0.4])
  .padUnit('domain');

var xExtent = fc.extentLinear()
  .accessors([function(d) { return d.x; }]);

// create a chart
var chart = fc.chartSvgCartesian(
    d3.scaleLinear(),
    d3.scaleLinear())
  .yDomain(yExtent(data))
  .yLabel('Sine / Cosine')
  .yOrient('left')
  .xDomain(xExtent(data))
  .xLabel('Value')
  .chartLabel('Sine/Cosine Line/Area Chart');

// create a pair of series and some gridlines
var sinLine = fc.seriesSvgLine()
  .crossValue(function(d) { return d.x; })
  .mainValue(function(d) { return d.y; })
  .decorate(function(selection) {
    selection.enter()
      .style('stroke', 'purple');
  });

var cosLine = fc.seriesSvgArea()
  .crossValue(function(d) { return d.x; })
  .mainValue(function(d) { return d.z; })
  .decorate(function(selection) {
    selection.enter()
      .style('fill', 'lightgreen')
      .style('fill-opacity', 0.5);
  });

var gridlines = fc.annotationSvgGridline();

// combine using a multi-series
var multi = fc.seriesSvgMulti()
  .series([gridlines, sinLine, cosLine]);

chart.plotArea(multi);

// render
d3.select('#simple-chart')
  .datum(data)
  .call(chart);

你可以在这个代码库中看到它的作用:

https://codepen.io/ColinEberhardt/pen/dOBvOy

在这里,您可以调整窗口的大小,并验证图表是否被正确重新渲染。

请注意,作为一个充分的披露,我是d3fc的维护者之一。

您还可以使用bootstrap 3来调整可视化的大小。例如,我们可以将HTML代码设置为:

<div class="container>
<div class="row">

<div class='col-sm-6 col-md-4' id="month-view" style="height:345px;">
<div id ="responsivetext">Something to write</div>
</div>

</div>
</div>

因为我的需要,我已经设置了一个固定的高度,但是你也可以保持大小自动。“col-sm-6 col-md-4”使div能够响应不同的设备。你可以在http://getbootstrap.com/css/#grid-example-basic上了解更多

我们可以在id month-view的帮助下访问该图。

我不会详细介绍d3代码,我只会输入适应不同屏幕尺寸所需的部分。

var width = document.getElementById('month-view').offsetWidth;

var height = document.getElementById('month-view').offsetHeight - document.getElementById('responsivetext2').offsetHeight;

宽度是通过id month-view获取div的宽度来设置的。

在我的例子中,高度不应该包括整个区域。我也有一些文字上面的酒吧,所以我需要计算的面积以及。这就是为什么我用id responsivetext标识文本区域的原因。为了计算栏的允许高度,我用div的高度减去文本的高度。

这允许你有一个条,将采用所有不同的屏幕/div大小。这可能不是最好的方法,但它肯定能满足我的项目的需要。

寻找“响应式SVG”,使SVG响应式非常简单,您不必再担心大小问题。

以下是我的做法:

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

CSS代码:

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

更多信息/教程:

http://demosthenes.info/blog/744/Make-SVG-Responsive

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