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

mth*_*pvg 170 javascript d3.js

我正在用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;
Run Code Online (Sandbox Code Playgroud)

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

var svg = d3.select("body").append("svg")
        .attr("width", x)
        .attr("height", y)
        .append("g");
Run Code Online (Sandbox Code Playgroud)

现在我想在用户调整窗口大小时,需要注意调整绘图的大小.

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

cmi*_*tti 277

寻找"响应式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);
Run Code Online (Sandbox Code Playgroud)

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

svg .rect {
  fill: gold;
  stroke: steelblue;
  stroke-width: 5px;
}
Run Code Online (Sandbox Code Playgroud)

更多信息/教程:

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

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

  • 关于UX的注意事项:对于某些用例,将基于SVG的d3图表视为具有固定宽高比的资产并不理想.显示文本,或者是动态的图表,或者通常感觉更像是适当的用户界面而非资产的图表,通过使用更新的维度重新渲染可以获得更多的图表.例如,viewBox强制轴字体和刻度放大/缩小,与html文本相比可能看起来很尴尬.相比之下,重新渲染使图表能够保持标记的一致大小,并且它提供了添加或删除刻度的机会. (36认同)
  • 只是添加到:`viewBox`属性应设置为SVG图的相关高度和宽度:`.attr("viewBox","0 0"+ width +""+ height)`where`width`和`height`在别处定义.也就是说,除非您希望SVG被视图框剪切.您可能还想更新css中的`padding-bottom`参数以匹配svg元素的宽高比.虽然有很好的反应 - 非常有帮助,并且很有效.谢谢! (12认同)
  • 基于相同的想法,给出不涉及保持纵横比的答案将是有趣的.问题是有一个svg元素在调整大小时保持覆盖整个窗口,这在大多数情况下意味着不同的宽高比. (12认同)
  • 这更加优雅,并且还使用容器缩放方法,应该在每个前端开发人员的工具包中(可以用于按特定宽高比缩放任何内容).应该是最好的答案. (8认同)

Ada*_*rce 43

使用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);
Run Code Online (Sandbox Code Playgroud)

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

  • 这不是一个好的答案,因为它涉及到DOM事件的绑定......更好的解决方案是仅使用d3和css (11认同)
  • @alem0lars 有趣的是,css/d3 版本对我不起作用,而这个版本对我有用...... (2认同)
  • 我知道它出现在 JSFiddle 中,但看到“w”、“e”、“g”不知从何而来,而“x”、“y”在答案中没有任何关键字的情况下被初始化,这真是令人困惑。 (2认同)

slf*_*slf 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'));

}
Run Code Online (Sandbox Code Playgroud)

http://eyeseast.github.io/visible-data/2013/08/28/responsive-charts-with-d3/

  • 这个答案绝不逊色。他们的答案将缩放图表的各个方面(包括刻度、轴、线条和文本注释),这些方面在某些屏幕尺寸下可能看起来很尴尬,在其他屏幕尺寸下看起来很糟糕,并且在极端尺寸下难以阅读。我发现使用此方法(或者,对于更现代的解决方案,@Angu Agarwal)调整大小更好。 (2认同)

Rai*_*aik 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...
}
Run Code Online (Sandbox Code Playgroud)

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

  • 但这是正确的:在渲染时,您可以确定是否应该插入轴、隐藏图例、根据可用内容执行其他操作。使用纯粹的 CSS/viewbox 解决方案,它所做的只是使可视化看起来被压扁。对图像有利,对数据不利。 (2认同)

gav*_*avs 8

在强制布局中,仅设置"高度"和"宽度"属性将无法将绘图重新居中/移动到svg容器中.然而,不是这样的发现力布局工作很简单的答案在这里.综上所述:

使用你喜欢的相同(任何)事件.

window.on('resize', resize);
Run Code Online (Sandbox Code Playgroud)

假设你有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();
}
Run Code Online (Sandbox Code Playgroud)

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

干杯,克