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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 03:58:30  来源:igfitidea点击:

Resize svg when window is resized in d3.js

javascriptd3.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:

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

http://jsfiddle.net/Zb85u/1/

http://jsfiddle.net/Zb85u/1/

回答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 sizemethod 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();