假设我有一个直方图脚本,构建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


当前回答

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

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

生成的HTML是这样的:

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

其他回答

还有另一种不需要重绘图形的方法,它涉及到修改<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内容将自动缩放。你可以在这里看到一个工作示例(进行了一些修改):调整窗口或右下角窗格的大小,看看它是如何反应的。

我写了一个小要点来解决这个问题。

一般的解决模式是这样的:

Breakout the script into computation and drawing functions. Ensure the drawing function draws dynamically and is driven of visualisation width and height variables (The best way to do this is to use the d3.scale api) Bind/chain the drawing to a reference element in the markup. (I used jquery for this, so imported it). Remember to remove it if it's already drawn. Get the dimensions from the referenced element using jquery. Bind/chain the draw function to the window resize function. Introduce a debounce (timeout) to this chain to ensure we only redraw after a timeout.

为了提高速度,我还添加了简化的d3.js脚本。 要点在这里:https://gist.github.com/2414111

Jquery参考回码:

$(reference).empty()
var width = $(reference).width();

防反跳代码:

var debounce = function(fn, timeout) 
{
  var timeoutID = -1;
  return function() {
     if (timeoutID > -1) {
        window.clearTimeout(timeoutID);
     }
   timeoutID = window.setTimeout(fn, timeout);
  }
};

var debounced_draw = debounce(function() {
    draw_histogram(div_name, pos_data, neg_data);
  }, 125);

 $(window).resize(debounced_draw);

享受吧!

我会避免像瘟疫一样调整大小/标记的解决方案,因为它们效率很低,并且会在你的应用程序中引起问题(例如,工具提示重新计算它应该出现在窗口调整大小上的位置,然后一会儿你的图表也会调整大小,页面重新布局,现在你的工具提示又错了)。

你可以在一些旧的浏览器中模拟这种行为,比如IE11,使用<canvas>元素来维护它的方面。

给定960x540,这是16:9的一个方面

<div style="position: relative">
  <canvas width="16" height="9" style="width: 100%"></canvas>
  <svg viewBox="0 0 960 540" preserveAspectRatio="xMidYMid meet" style="position: absolute; top: 0; right: 0; bottom: 0; left: 0; -webkit-tap-highlight-color: transparent;">
  </svg>
</div>

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

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

生成的HTML是这样的:

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

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的维护者之一。