Resize svg when window is resized in d3.js
我正在用d3.js画一个散点图。 在这个问题的帮助下:
获取屏幕大小,当前网页和浏览器窗口
我正在使用这个答案:
1 2 3 4 5 6 | 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; |
所以我能够将我的情节适合用户的窗口,如下所示:
1 2 3 4 | var svg = d3.select("body").append("svg") .attr("width", x) .attr("height", y) .append("g"); |
现在我想在用户调整窗口大小时,需要注意调整绘图的大小。
PS:我的代码中没有使用jQuery。
寻找"响应式SVG"使SVG响应变得非常简单,您不必再担心大小了。
我是这样做的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | .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; } |
1 | <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"> |
注意:SVG图像中的所有内容都将随窗口宽度缩放。这包括笔划宽度和字体大小(甚至是使用CSS设置的)。如果不希望如此,下面将介绍更多的替代解决方案。
更多信息/教程:
http://thenewcode.com/744/Make-SVG-Responsive
http://soqr.fr/testsvg/embed-svg-liquid-layout-responsive-web-design.php
使用window.onresize:
1 2 3 4 5 6 7 | 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的新方法
历史目的的旧答案
IMO最好使用select()和on(),因为这样你可以拥有多个resize事件处理程序......只是不要太疯狂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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/
如果调整大小代码几乎与首先构建图形的代码一样长,那就太难看了。因此,为什么不简单地重新加载它,而不是调整现有图表的每个元素的大小?以下是它对我有用的方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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... } |
关键线是
在强制布局中,仅设置"高度"和"宽度"属性将无法将绘图重新居中/移动到svg容器中。但是,这里有一个非常简单的答案,适用于Force Layouts。综上所述:
使用你喜欢的相同(任何)事件。
1 | window.on('resize', resize); |
假设你有svg和force变量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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根据需要重新计算。这至少在您使用重力点时起作用。我不确定这是否是此解决方案的先决条件。任何人都可以确认或否认吗?
干杯,克
如果要将自定义逻辑绑定到resize事件,现在您可以开始将ResizeObserver浏览器API用于SVGElement的边界框。
这也将处理由于附近元素大小更改而调整容器大小的情况。
有一个polyfill可以提供更广泛的浏览器支持。
这是它在UI组件中的工作方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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) |
1 2 3 4 5 6 7 8 9 10 | .graph-container { height: 75vh; width: 75vw; } .graph-container svg rect { fill: gold; stroke: steelblue; stroke-width: 3px; } |
1 2 3 4 5 6 7 8 | <script src="https://unpkg.com/[email protected]/dist/ResizeObserver.js"> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"> <figure class="graph-container"> <svg width="100" height="100"> <rect x="0" y="0" width="100" height="100" /> </svg> </figure> |
1 2 | // unobserve in component destroy method this.resizeObserver.disconnect() |
对于那些在D3 v4 / v5中使用力导向图的人,
1 2 3 4 5 | 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作为客户端的高度。