javascript 在 d3.js 中调整窗口大小时调整 svg
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16265123/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Resize svg when window is resized in d3.js
提问by mthpvg
I'm drawing a scatterplot with d3.js. With the help of this question :
Get the size of the screen, current web page and browser window
我正在用 d3.js 绘制散点图。在这个问题的帮助下:
获取屏幕大小、当前网页和浏览器窗口
I'm using this answer :
我正在使用这个答案:
var w = window,
d = document,
e = d.documentElement,
g = d.getElementsByTagName('body')[0],
x = w.innerWidth || e.clientWidth || g.clientWidth,
y = w.innerHeight|| e.clientHeight|| g.clientHeight;
So I'm able to fit my plot to the user's window like this :
所以我可以像这样将我的情节适合用户的窗口:
var svg = d3.select("body").append("svg")
.attr("width", x)
.attr("height", y)
.append("g");
Now I'd like that something takes care of resizing the plot when the user resize the window.
现在我希望在用户调整窗口大小时可以调整绘图大小。
PS : I'm not using jQuery in my code.
PS:我没有在我的代码中使用 jQuery。
回答by cminatti
Look for 'responsive SVG' it is pretty simple to make a SVG responsive and you don't have to worry about sizes any more.
寻找“响应式 SVG”,使 SVG 响应式非常简单,您不必再担心尺寸。
Here is how I did it:
这是我如何做到的:
d3.select("div#chartId")
.append("div")
// Container class to make it responsive.
.classed("svg-container", true)
.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)
// Fill with a rectangle for visualization.
.append("rect")
.classed("rect", true)
.attr("width", 600)
.attr("height", 400);
.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;
}
svg .rect {
fill: gold;
stroke: steelblue;
stroke-width: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="chartId"></div>
Note:Everything in the SVG image will scale with the window width. This includes stroke width and font sizes (even those set with CSS). If this is not desired, there are more involved alternate solutions below.
注意:SVG 图像中的所有内容都将随窗口宽度缩放。这包括笔画宽度和字体大小(即使是那些用 CSS 设置的)。如果不希望这样,下面有更多涉及的替代解决方案。
More info / tutorials:
更多信息/教程:
http://thenewcode.com/744/Make-SVG-Responsive
http://thenewcode.com/744/Make-SVG-Responsive
http://soqr.fr/testsvg/embed-svg-liquid-layout-responsive-web-design.php
http://soqr.fr/testsvg/embed-svg-liquid-layout-responsive-web-design.php
回答by Adam Pearce
Use window.onresize:
function updateWindow(){
x = w.innerWidth || e.clientWidth || g.clientWidth;
y = w.innerHeight|| e.clientHeight|| g.clientHeight;
svg.attr("width", x).attr("height", y);
}
d3.select(window).on('resize.updatesvg', updateWindow);
回答by slf
UPDATEjust use the new way from @cminatti
更新只是使用@cminatti 的新方法
old answer for historic purposes
出于历史目的的旧答案
IMO it's better to use select() and on() since that way you can have multiple resize event handlers... just don't get too crazy
IMO 最好使用 select() 和 on() 因为这样你可以有多个调整大小的事件处理程序......只是不要太疯狂
d3.select(window).on('resize', resize);
function resize() {
// update width
width = parseInt(d3.select('#chart').style('width'), 10);
width = width - margin.left - margin.right;
// resize the chart
x.range([0, width]);
d3.select(chart.node().parentNode)
.style('height', (y.rangeExtent()[1] + margin.top + margin.bottom) + 'px')
.style('width', (width + margin.left + margin.right) + 'px');
chart.selectAll('rect.background')
.attr('width', width);
chart.selectAll('rect.percent')
.attr('width', function(d) { return x(d.percent); });
// update median ticks
var median = d3.median(chart.selectAll('.bar').data(),
function(d) { return d.percent; });
chart.selectAll('line.median')
.attr('x1', x(median))
.attr('x2', x(median));
// update axes
chart.select('.x.axis.top').call(xAxis.orient('top'));
chart.select('.x.axis.bottom').call(xAxis.orient('bottom'));
}
http://eyeseast.github.io/visible-data/2013/08/28/responsive-charts-with-d3/
http://eyeseast.github.io/visible-data/2013/08/28/responsive-charts-with-d3/
回答by Raik
It's kind of ugly if the resizing code is almost as long as the code for building the graph in first place. So instead of resizing every element of the existing chart, why not simply reloading it? Here is how it worked for me:
如果调整大小的代码几乎与首先构建图形的代码一样长,那就有点难看。因此,与其调整现有图表的每个元素的大小,为什么不简单地重新加载它呢?这是它对我的工作方式:
function data_display(data){
e = document.getElementById('data-div');
var w = e.clientWidth;
// remove old svg if any -- otherwise resizing adds a second one
d3.select('svg').remove();
// create canvas
var svg = d3.select('#data-div').append('svg')
.attr('height', 100)
.attr('width', w);
// now add lots of beautiful elements to your graph
// ...
}
data_display(my_data); // call on page load
window.addEventListener('resize', function(event){
data_display(my_data); // just call it again...
}
The crucial line is d3.select('svg').remove();
. Otherwise each resizing will add another SVG element below the previous one.
关键线是d3.select('svg').remove();
。否则,每次调整大小都会在前一个元素下方添加另一个 SVG 元素。
回答by gavs
In force layouts simply setting the 'height' and 'width' attributes will not work to re-center/move the plot into the svg container. However, there's a very simple answer that works for Force Layouts found here. In summary:
在强制布局中,简单地设置“高度”和“宽度”属性将无法将绘图重新居中/移动到 svg 容器中。然而,有一个非常简单的答案,对于力布局的作品找到这里。总之:
Use same (any) eventing you like.
使用您喜欢的相同(任何)事件。
window.on('resize', resize);
Then assuming you have svg & force variables:
然后假设你有 svg & force 变量:
var svg = /* D3 Code */;
var force = /* D3 Code */;
function resize(e){
// get width/height with container selector (body also works)
// or use other method of calculating desired values
var width = $('#myselector').width();
var height = $('#myselector').height();
// set attrs and 'resume' force
svg.attr('width', width);
svg.attr('height', height);
force.size([width, height]).resume();
}
In this way, you don't re-render the graph entirely, we set the attributes and d3 re-calculates things as necessary. This at least works when you use a point of gravity. I'm not sure if that's a prerequisite for this solution. Can anyone confirm or deny ?
通过这种方式,您不必完全重新渲染图形,我们设置属性,d3 会根据需要重新计算。这至少在您使用重力点时有效。我不确定这是否是此解决方案的先决条件。任何人都可以确认或否认吗?
Cheers, g
干杯,克
回答by Anbu Agarwal
If you want to bind custom logic to resize event, nowadaysyou may start using ResizeObserver browser APIfor the bounding box of an SVGElement.
This will also handle the case when container is resized because of the nearby elements size change.
There is a polyfillfor broader browser support.
如果您想绑定自定义逻辑来调整大小事件,现在您可以开始使用ResizeObserver 浏览器 API来处理SVGElement的边界框。
这也将处理由于附近元素大小变化而调整容器大小的情况。
有一个polyfill 可以提供更广泛的浏览器支持。
This is how it may work in UI component:
这就是它在 UI 组件中的工作方式:
function redrawGraph(container, { width, height }) {
d3
.select(container)
.select('svg')
.attr('height', height)
.attr('width', width)
.select('rect')
.attr('height', height)
.attr('width', width);
}
// Setup observer in constructor
const resizeObserver = new ResizeObserver((entries, observer) => {
for (const entry of entries) {
// on resize logic specific to this component
redrawGraph(entry.target, entry.contentRect);
}
})
// Observe the container
const container = document.querySelector('.graph-container');
resizeObserver.observe(container)
.graph-container {
height: 75vh;
width: 75vw;
}
.graph-container svg rect {
fill: gold;
stroke: steelblue;
stroke-width: 3px;
}
<script src="https://unpkg.com/[email protected]/dist/ResizeObserver.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<figure class="graph-container">
<svg width="100" height="100">
<rect x="0" y="0" width="100" height="100" />
</svg>
</figure>
// unobserve in component destroy method
this.resizeObserver.disconnect()
回答by Justin Lewis
For those using force directed graphs in D3 v4/v5, the size
method doesn't exist any more. Something like the following worked for me (based on this github issue):
对于那些在 D3 v4/v5 中使用力有向图的人,该size
方法不再存在。类似以下内容对我有用(基于此 github 问题):
simulation
.force("center", d3.forceCenter(width / 2, height / 2))
.force("x", d3.forceX(width / 2))
.force("y", d3.forceY(height / 2))
.alpha(0.1).restart();