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

时间:2013-04-28 16:37:32

标签: javascript d3.js

我正在用d3.js画一个散点图。在这个问题的帮助下:
Get the size of the screen, current web page and browser window

我正在使用这个答案:

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;

所以我能够将我的情节适合用户的窗口,如下所示:

var svg = d3.select("body").append("svg")
        .attr("width", x)
        .attr("height", y)
        .append("g");

现在我想要在用户调整窗口大小时调整图表的大小。

PS:我的代码中没有使用jQuery。

9 个答案:

答案 0 :(得分:265)

寻找'响应式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://thenewcode.com/744/Make-SVG-Responsive

http://soqr.fr/testsvg/embed-svg-liquid-layout-responsive-web-design.php

答案 1 :(得分:41)

使用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/

答案 2 :(得分:32)

更新只需使用@cminatti的新方式


用于历史目的的旧答案

IMO最好使用select()和on(),因为这样你可以拥有多个resize事件处理程序......只是不要太疯狂

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/

答案 3 :(得分:12)

如果调整大小代码几乎与首先构建图形的代码一样长,那就太难看了。因此,为什么不简单地重新加载它,而不是调整现有图表的每个元素的大小?以下是它对我有用的方法:

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...
}

关键线是d3.select('svg').remove();。否则,每次调整大小都会在前一个之后添加另一个SVG元素。

答案 4 :(得分:8)

在强制布局中,只需设置' height'和#'宽度'属性不能用于重新居中/将绘图移动到svg容器中。但是,有一个非常简单的答案适用于here强制布局。总结:

使用相同的(任意)事件。

window.on('resize', resize);

然后假设你有svg&强制变量:

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();
}

通过这种方式,您不会完全重新渲染图形,我们设置属性并根据需要重新计算内容。这至少在您使用重力点时起作用。我不确定这是否是此解决方案的先决条件。任何人都可以确认或否认吗?

干杯,g

答案 5 :(得分:1)

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>

答案 6 :(得分:0)

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

答案 7 :(得分:0)

如果您想将自定义逻辑绑定到事件大小调整中,nowadays可以开始将ResizeObserver browser API用作SVGElement的边界框。
当附近的元素大小更改而调整容器大小时,这也将处理。
polyfill用于更广泛的浏览器支持。

这是它在UI组件中的工作方式:

<div class="container">
  <svg></svg>
</div>

组件:

// Setup observer in constructor
const container = document.querySelector('.container')

const resizeObserver = new ResizeObserver((entries, observer) => {
  // on resize logic specific to this component
})

resizeObserver.observe(container)

// unobserve in component destroy method
this.resizeObserver.disconnect()

答案 8 :(得分:0)

对于在D3 v4 / v5中使用力向图的用户来说,size方法不再存在。以下内容对我有用(基于this github issue):

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();