我正在用d3.js绘制散点图。借助这个问题: 获取屏幕大小,当前网页和浏览器窗口

我用的答案是:

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。


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


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


出于历史目的的旧答案

在我看来,最好使用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/


如果调整大小的代码几乎和最初构建图形的代码一样长,那就有点难看了。因此,与其调整现有图表的每个元素的大小,为什么不简单地重新加载它呢?我是这样做的:

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元素的下面添加另一个SVG元素。


在强制布局中,简单地设置“height”和“width”属性将无法将图形重新居中/移动到svg容器中。然而,这里有一个非常简单的答案,适用于Force Layouts。总而言之:

使用你喜欢的(任何)项目。

window.on('resize', resize);

然后假设你有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();
}

通过这种方式,你不需要完全重新渲染图形,我们设置属性,d3根据需要重新计算东西。至少当你使用一个重力点时,这是可行的。我不确定这是否是这个解决方案的先决条件。有人能证实或否认吗?

欢呼,克


寻找“响应式SVG”,使SVG响应式非常简单,您不必再担心大小问题。

以下是我的做法:

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>

注意:SVG图像中的所有内容都将随窗口宽度缩放。这包括笔画宽度和字体大小(甚至是那些用CSS设置的)。如果不希望这样做,下面还有更多涉及的替代解决方案。

更多信息/教程:

http://thenewcode.com/744/Make-SVG-Responsive

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


如果你想绑定自定义逻辑来调整事件大小,现在你可以开始使用ResizeObserver浏览器API来调整SVGElement的边界框。 这也将处理容器因附近元素大小变化而调整大小的情况。 有一个用于更广泛的浏览器支持的填充。

这是它在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()

对于那些在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();