使 d3.js 可视化布局响应的最佳方法是什么?

IT技术 javascript responsive-design d3.js
2021-01-16 20:33:59

假设我有一个构建 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

6个回答

还有另一种不需要重新绘制图形的方法,它涉及修改元素上的viewBoxpreserveAspectRatio属性<svg>

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

2015 年 11 月 24 日更新:大多数现代浏览器都可以从推断出SVG 元素的纵横比viewBox,因此您可能不需要使图表的大小保持最新。如果您需要支持旧浏览器,您可以在窗口调整大小时调整元素大小,如下所示:

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 内容将自动缩放。你可以看到这样一个工作示例(有一些修改)这里:只是调整窗口大小或看看它是如何react的底部右侧窗格中。

我真的很喜欢这种方法 shawn,2 个问题。1 视图框是否跨浏览器工作?2. 在您的示例中,圆圈会调整大小但不适合窗口。svg 视图框或纵横比是否有问题。
2021-03-19 20:33:59
实际上仅将 viewBox 和 preserveAspectRatio 设置为宽度高度设置为父级的 100% 的 SVG,并且父级作为引导程序/弹性网格对我有用,而无需处理调整大小事件
2021-03-19 20:33:59
喜欢这种方法,不确定上述方法是否正确。正如马特所说,圆圈会重新调整大小,但从图表左侧到第一个圆圈的距离不会以与圆圈相同的速度调整大小。我尝试在 jsfiddle 中摆弄一些,但使用 Google Chrome 无法按预期工作。viewBox 和 preserveAspectRatio 与哪些浏览器兼容?
2021-03-31 20:33:59
注意:在几个 d3 教程中 1.) 在 html 中设置了 svg 宽度和高度 2.) 可视化是通过onload 函数调用进行的。如果在实际 html 中设置了 svg 宽度和高度,并且您使用上述技术,则图像将无法缩放。
2021-04-04 20:33:59
确认 Deepu 是正确的。适用于 Bootstrap/flex 网格。谢谢@Deepu。
2021-04-07 20:33:59

寻找“响应式 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

你能看看这个问题吗stackoverflow.com/questions/67312494/...
2021-03-31 20:33:59
就我而言, padding-bottom: 100% 将使我的容器在底部有额外的填充,因此我将其更改为 50% 以将其删除。使用您的代码并成功使 svg 响应,谢谢。
2021-04-10 20:33:59

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

一般的解决方案是这样的:

  1. 将脚本分解为计算和绘图功能。
  2. 确保绘制函数动态绘制并由可视化宽度和高度变量驱动(最好的方法是使用 d3.scale api)
  3. 将绘图绑定/链接到标记中的参考元素。(我为此使用了 jquery,因此导入了它)。
  4. 如果它已经绘制,请记住将其删除。使用 jquery 从引用的元素中获取维度。
  5. 将绘制函数绑定/链接到窗口调整大小函数。向该链引入去抖动(超时)以确保我们仅在超时后重绘。

我还添加了缩小的 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);

享受!

SVG 是一种矢量格式。您可以设置任何虚拟视框大小,然后将其缩放到实际尺寸。无需使用JS。
2021-03-14 20:33:59
这似乎不起作用;如果我调整包含在更正要点中的 html 文件的窗口大小,随着窗口变小,直方图仍然会被切断......
2021-03-24 20:33:59

不使用 ViewBox

以下是不依赖于使用的解决方案示例viewBox

关键在于更新用于放置数据比例尺范围

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

var ratio = width / height;

然后,在每个调整大小,更新rangexy

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

请注意,高度基于宽度和纵横比,以便保持原始比例。

最后,“重绘”图表——更新任何依赖于xy比例的属性

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

请注意,在重新调整大小的rects可以使用上限的rangey,而不是明确地使用高度:

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

肖恩艾伦的回答很棒。但是您可能不想每次都这样做。如果您将它托管在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 上构建了此功能