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


当前回答

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

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

其他回答

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

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包装器的情况下,如plottable.js,请注意,最简单的解决方案可能是添加一个事件监听器,然后调用重绘制函数(在plottable.js中重绘制)。在plottable.js的情况下,这将非常有效(这种方法很少有文档):

    window.addEventListener("resize", function() {
      table.redraw();
    });

肖恩·阿伦的回答很棒。但你可能不想每次都这么做。如果你把它放在vida上。Io,您可以自动响应SVG可视化。

你可以用这个简单的嵌入代码获得响应式iframe:

<div id="vida-embed">
<iframe src="http://embed.vida.io/documents/9Pst6wmB83BgRZXgx" width="auto" height="525" seamless frameBorder="0" scrolling="no"></iframe>
</div>

#vida-embed iframe {
  position: absolute;
  top:0;
  left: 0;
  width: 100%;
  height: 100%;
}

http://jsfiddle.net/dnprock/npxp3v9d/1/

披露:我在vida.io上构建了这个功能。

不使用ViewBox

下面是一个不依赖viewBox的解决方案的例子:

关键在于更新用于放置数据的尺度范围。

首先,计算你的原始纵横比:

var ratio = width / height;

然后,在每次调整大小时,更新x和y的范围:

function resize() {
  x.rangeRoundBands([0, window.innerWidth]);
  y.range([0, window.innerWidth / ratio]);
  svg.attr("height", window.innerHeight);
}

请注意,高度是基于宽度和长宽比,所以你的原始比例是保持不变的。

最后,“重绘”图表——更新任何依赖于x或y刻度的属性:

function redraw() {
    rects.attr("width", x.rangeBand())
      .attr("x", function(d) { return x(d.x); })
      .attr("y", function(d) { return y.range()[1] - y(d.y); })
      .attr("height", function(d) { return y(d.y); });
}

注意,在调整矩形的大小时,你可以使用y范围的上界,而不是显式地使用height:

.attr("y", function(d) { return y.range()[1] - y(d.y); })

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 ratio = width / height; var x = d3.scale.ordinal() .domain(histogram.map(function(d) { return d.x; })) var y = d3.scale.linear() .domain([0, d3.max(histogram, function(d) { return d.y; })]) var svg = d3.select("body").append("svg") .attr("width", "100%") .attr("height", height); var rects = svg.selectAll("rect").data(histogram); rects.enter().append("rect"); function redraw() { rects.attr("width", x.rangeBand()) .attr("x", function(d) { return x(d.x); }) // .attr("y", function(d) { return height - y(d.y); }) .attr("y", function(d) { return y.range()[1] - y(d.y); }) .attr("height", function(d) { return y(d.y); }); } function resize() { x.rangeRoundBands([0, window.innerWidth]); y.range([0, window.innerWidth / ratio]); svg.attr("height", window.innerHeight); } d3.select(window).on('resize', function() { resize(); redraw(); }) resize(); redraw(); <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

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

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

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

享受吧!