d3.js에서 창 크기를 조정할 때 svg 크기 조정


183

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

이제 사용자가 창 크기를 조정할 때 플롯의 크기를 조정하는 것이 필요합니다.

추신 : 내 코드에서 jQuery를 사용하지 않습니다.


답변:


295

'반응 형 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


8
이것은 훨씬 더 우아하며 모든 프런트 엔드 개발자 툴킷에 있어야하는 컨테이너 스케일링 접근 방식을 사용합니다 (특정 종횡비의 모든 것을 스케일링하는 데 사용할 수 있음). 최고의 답변이어야합니다.
kontur

13
그냥이에 추가하려면 viewBox: 속성이 당신의 SVG 플롯의 관련 높이와 너비로 설정해야 하고 다른 곳에서 정의된다. 즉,보기 상자로 SVG를 클리핑하지 않는 한. svg 요소의 종횡비와 일치하도록 CSS 의 매개 변수 를 업데이트 할 수도 있습니다 . 그럼에도 불구하고 탁월한 응답-매우 도움이되며 치료를합니다. 감사! .attr("viewBox","0 0 " + width + " " + height)widthheightpadding-bottom
앤드류 가이

13
같은 생각을 바탕으로 가로 세로 비율을 유지하지 않는 대답을하는 것이 흥미로울 것입니다. 문제는 크기 조정시 전체 창을 계속 덮는 svg 요소가 있다는 것입니다. 대부분의 경우 다른 종횡비를 의미합니다.
Nicolas Le Thierry d' Ennequin

37
UX 관련 참고 사항 : 일부 사용 사례의 경우 SVG 기반 d3 차트를 고정 종횡비의 자산으로 취급하는 것이 이상적이지 않습니다. 텍스트를 표시하거나 동적이거나 일반적으로 자산보다 적절한 사용자 인터페이스처럼 느껴지는 차트는 업데이트 된 차원으로 다시 렌더링하여 얻을 수 있습니다. 예를 들어 viewBox는 축 글꼴과 눈금을 강제로 확대 / 축소하여 html 텍스트에 비해 어색하게 보일 수 있습니다. 반대로 다시 렌더링을 사용하면 차트의 레이블 크기를 일관된 크기로 유지하고 눈금을 추가하거나 제거 할 수 있습니다.
Karim Hernandez

1
top: 10px;? 나에게 잘못된 것 같고 오프셋을 제공합니다. 0px로 설정하면 정상적으로 작동합니다.
TimZaman

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

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


11
DOM 이벤트에 대한 입찰을 포함하기 때문에 이것은 좋은 대답이 아닙니다 ... 훨씬 더 나은 해결책은 d3과 CSS 만 사용하는 것입니다.
alem0lars

@ alem0lars 재미있게도, css / d3 버전은 나를 위해 작동하지 않았고 이것은 하나 ...
Christian

32

업데이트 는 @cminatti의 새로운 방식을 사용합니다.


역사적인 목적을위한 오래된 대답

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/


나는 덜 동의 할 수 없습니다. cminatti의 방법은 우리가 다루는 모든 d3js 파일에서 작동합니다. 차트 당 크기 조정으로 변환하는이 방법은 과도합니다. 또한 생각할 수있는 모든 가능한 차트에 대해 다시 작성해야합니다. 위에서 언급 한 방법은 모든 것에 적용됩니다. 나는 당신이 언급 한 재 장전 유형을 포함하여 4 가지 레시피를 시도했지만 그중 어느 것도 입체파에 사용되는 유형과 같은 다중 영역 플롯에서 작동하지 않습니다.
Eamonn Kenny

1
@EamonnKenny 더 이상 당신과 동의 할 수 없습니다. 다른 답변 7 개월 미래에 우수합니다 :)
slf

이 방법의 경우 : "d3.select (window) .on" "d3.select (window) .off"또는 "d3.select (window) .unbind"를 찾을 수 없음
sea-kg


이 대답은 결코 열등하지 않습니다. 그들의 대답은 그래프의 모든 측면 (틱, 축, 선 및 텍스트 주석 포함)을 스케일링하여 어색하게 보일 수 있으며, 일부 화면 크기에서는 다른 화면에서는 나쁘고 극단적 인 크기에서는 읽을 수 없습니다. 이 (또는 더 현대적인 솔루션 인 @Angu Agarwal) 방법을 사용하여 크기를 조정하는 것이 좋습니다.
Rúnar Berg 2016 년

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 아래에 다른 SVG 요소가 추가됩니다.


모든 창 크기 조정 이벤트에서 전체 요소를 다시
그리면

2
그러나 이것은 정확합니다. 렌더링 할 때 삽입 축이 있어야하는지, 범례를 숨기고 사용 가능한 내용을 기반으로 다른 작업을 수행해야하는지 확인할 수 있습니다. 순전히 CSS / 뷰 박스 솔루션을 사용하면 비주얼라이제이션이 찌그러져 보이게됩니다. 이미지에는 좋고 데이터에는 좋지 않습니다.
Chris Knoll

8

강제 레이아웃에서 단순히 '높이'및 '너비'속성을 설정하면 플롯을 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는 필요에 따라 항목을 다시 계산합니다. 이것은 적어도 중력 지점을 사용할 때 작동합니다. 이것이이 솔루션의 전제 조건인지 확실하지 않습니다. 누구나 확인하거나 거부 할 수 있습니까?

건배, g


이것은 약 100 개의 연결이있는 내 힘 레이아웃에서 완벽하게 작동했습니다. 감사.
tribe84

1

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

1

이벤트 크기를 조정하기 위해 사용자 지정 논리를 바인딩하려면 요즘 SVGElement 의 경계 상자에 ResizeObserver 브라우저 API 를 사용할 수 있습니다 .
주변 요소 크기 변경으로 인해 컨테이너 크기가 조정되는 경우도 처리합니다. 광범위한 브라우저 지원을위한 폴리 필
이 있습니다 .

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()
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.