简体   繁体   English

在 d3.js 中调整窗口大小时调整 svg

[英]Resize svg when window is resized in d3.js

I'm drawing a scatterplot with d3.js.我正在用 d3.js 绘制散点图。 With the help of this question :在这个问题的帮助下:
Get the size of the screen, current web page and browser window 获取屏幕大小、当前网页和浏览器窗口

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。

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.注意: SVG 图像中的所有内容都将随窗口宽度缩放。 This includes stroke width and font sizes (even those set with CSS).这包括笔画宽度和字体大小(即使是那些用 CSS 设置的)。 If this is not desired, there are more involved alternate solutions below.如果不希望这样,下面有更多涉及的替代解决方案。

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

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/

UPDATE just 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/

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();关键的一行是d3.select('svg').remove(); . . Otherwise each resizing will add another SVG element below the previous one.否则,每次调整大小都会在前一个元素下方添加另一个 SVG 元素。

In force layouts simply setting the 'height' and 'width' attributes will not work to re-center/move the plot into the svg container.在强制布局中,简单地设置“高度”和“宽度”属性将无法将绘图重新居中/移动到 svg 容器中。 However, there's a very simple answer that works for Force Layouts found here .然而,有一个非常简单的答案,对于力布局的作品找到这里 In summary:总之:

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.通过这种方式,您无需完全重新渲染图形,我们设置属性,d3 会根据需要重新计算。 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 ?任何人都可以确认或否认吗?

Cheers, g干杯,克

If you want to bind custom logic to resize event, nowadays you may start usingResizeObserver browser API for the bounding box of an SVGElement.如果您想绑定自定义逻辑来调整大小事件,现在您可以开始使用ResizeObserver 浏览器 API来处理SVGElement的边界框。
This will also handle the case when container is resized because of the nearby elements size change.这也将处理由于附近元素大小变化而调整容器大小的情况。
There is a polyfill for broader browser support.有一个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/resize-observer-polyfill@1.5.1/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()

For those using force directed graphs in D3 v4/v5, the size method doesn't exist any more.对于在 D3 v4/v5 中使用力有向图的人,不再存在size方法。 Something like the following worked for me (based on this github issue ):类似以下内容对我有用(基于此 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();

我使用window.innerWidth作为客户端的宽度,window.innerHeight作为客户端的高度。

 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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM