假设我有一个直方图脚本,构建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”,使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
我写了一个小要点来解决这个问题。
一般的解决模式是这样的:
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);
享受吧!
还有另一种不需要重绘图形的方法,它涉及到修改<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内容将自动缩放。你可以在这里看到一个工作示例(进行了一些修改):调整窗口或右下角窗格的大小,看看它是如何反应的。
您还可以使用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大小。这可能不是最好的方法,但它肯定能满足我的项目的需要。
不使用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>