假设我有一个直方图脚本,可以构建一个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
答案 0 :(得分:302)
还有另一种方法可以不需要重新绘制图表,而且还需要修改<svg>
元素上的viewBox和preserveAspectRatio属性:
<svg id="chart" width="960" height="500"
viewBox="0 0 960 500"
preserveAspectRatio="xMidYMid meet">
</svg>
2015年11月24日更新:大多数现代浏览器可以viewBox
添加infer the aspect ratio个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内容将自动缩放。您可以看到一个有效的示例(经过一些修改)here:只需调整窗口大小或右下方窗格,看看它是如何反应的。
答案 1 :(得分:30)
寻找响应式SVG&#39;使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
答案 2 :(得分:20)
我编写了一个小小的要点来解决这个问题。
一般的解决方案模式是:
我还为速度添加了缩小的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);
享受!
答案 3 :(得分:4)
以下是不依赖于使用viewBox
的解决方案示例:
关键是更新用于放置数据的比例的范围。
首先,计算您的原始宽高比:
var ratio = width / height;
然后,在每次调整大小时,更新range
和x
的{{1}}:
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); });
}
的大小时,您可以使用rects
的{{1}}的上限,而不是明确使用高度:
range
y
.attr("y", function(d) { return y.range()[1] - y(d.y); })
答案 4 :(得分:3)
如果您通过c3.js使用d3.js,则响应问题的解决方案非常简单:
var chart = c3.generate({bindTo:"#chart",...});
chart.resize($("#chart").width(),$("#chart").height());
生成的HTML如下所示:
<div id="chart">
<svg>...</svg>
</div>
答案 5 :(得分:2)
您可以使用这个简单的嵌入代码获取响应式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构建此功能。
答案 6 :(得分:2)
如果您使用的是 d3包装,例如plottable.js,请注意最简单的解决方案可能是添加事件监听器,然后调用重绘功能(plottable.js中为redraw
)。在plottable.js的情况下,这将非常有效(这种方法记录很少):
window.addEventListener("resize", function() {
table.redraw();
});
答案 7 :(得分:1)
如果人们还在访问这个问题 - 这就是对我有用的东西:
将iframe包含在div中并使用css为该div添加40%的填充(百分比取决于您想要的宽高比)。然后将iframe本身的宽度和高度都设置为100%。
在包含要在iframe中加载的图表的html doc中,将width设置为svg附加到的div的宽度(或者设置为body的宽度),并将height设置为width * aspect比。
编写一个在窗口调整大小时重新加载iframe内容的函数,以便在人们旋转手机时调整图表的大小。
我网站上的示例: http://dirkmjk.nl/en/2016/05/embedding-d3js-charts-responsive-website
2016年12月30日更新
我上面描述的方法有一些缺点,特别是它没有考虑任何不属于D3创建的svg的标题和标题的高度。我已经发现了我认为更好的方法:
我网站上的示例:http://dirkmjk.nl/en/2016/12/embedding-d3js-charts-responsive-website-better-solution
答案 8 :(得分:0)
D3数据连接的基本原则之一是它是幂等的。换句话说,如果使用相同的数据重复评估数据连接,则渲染的输出是相同的。因此,只要您正确渲染图表,请注意输入,更新和退出选择 - 当尺寸更改时您需要做的就是重新渲染图表。
还有一些其他的事情你应该做,一个是取消反弹窗口调整大小处理程序以限制它。此外,不应硬编码宽度/高度,而应通过测量包含元素来实现。
作为替代方案,这里是使用d3fc呈现的图表,https://codepen.io/ColinEberhardt/pen/dOBvOy是一组正确处理数据连接的D3组件。它还有一个笛卡尔图表,用它来衡量它包含的元素,使其易于创建和响应&#39;图表:
// 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);
您可以在此代码中看到它的实际效果:
您可以在其中调整窗口大小并验证图表是否已正确重新呈现。
请注意,作为完整的披露,我是d3fc的维护者之一。
答案 9 :(得分:0)
我会避免像瘟疫这样的调整大小/滴答解决方案,因为它们效率低下并且可能会导致应用程序出现问题(例如工具提示会重新计算它应该在窗口调整大小时显示的位置,然后片刻之后您的图表也会调整大小并且页面重新布局,现在你的工具提示又错了。)
您可以在某些较旧的浏览器中模拟此行为,这些浏览器也不像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>
答案 10 :(得分:0)
很多复杂的答案。
基本上,您需要做的就是抛弃width
和height
属性,转而使用viewBox
属性:
width = 500;
height = 500;
const svg = d3
.select("#chart")
.append("svg")
.attr("viewBox", `0 0 ${width} ${height}`)
如果有边距,可以将其添加到宽度/高度中,然后再附加g
,然后像通常那样对其进行变换。
答案 11 :(得分:-1)
您还可以使用 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>
由于我的需要,我已经设置了一个固定的高度,但你也可以保持自动尺寸。 &#34; col-sm-6 col-md-4&#34;使div响应不同的设备。您可以在http://getbootstrap.com/css/#grid-example-basic
了解详情我们可以借助ID 月视图来访问图表。
我不会详细介绍d3代码,我只会输入适应不同屏幕尺寸所需的部分。
var width = document.getElementById('month-view').offsetWidth;
var height = document.getElementById('month-view').offsetHeight - document.getElementById('responsivetext2').offsetHeight;
通过使用id月视图获取div的宽度来设置宽度。
我的情况下的高度不应该包括整个区域。我在条形图上方也有一些文字,所以我也需要计算这个区域。这就是我用id响应文本识别文本区域的原因。为了计算条的允许高度,我从div的高度减去了文本的高度。
这允许您拥有一个采用所有不同屏幕/ div大小的条形图。它可能不是最好的方式,但它肯定能满足我的项目需求。