d3.js 시각화 레이아웃을 반응 형으로 만드는 가장 좋은 방법은 무엇입니까?


224

960 500 svg 그래픽을 작성하는 히스토그램 스크립트가 있다고 가정합니다. 그래픽 크기와 높이를 동적으로 조정할 때 어떻게 반응하게합니까?

<script> 

var n = 10000, // number of trials
    m = 10,    // number of random variables
    data = [];

// Generate an Irwin-Hall distribution.
for (var i = 0; i < n; i++) {
  for (var s = 0, j = 0; j < m; j++) {
    s += Math.random();
  }
  data.push(s);
}

var histogram = d3.layout.histogram()
    (data);

var width = 960,
    height = 500;

var x = d3.scale.ordinal()
    .domain(histogram.map(function(d) { return d.x; }))
    .rangeRoundBands([0, width]);

var y = d3.scale.linear()
    .domain([0, d3.max(histogram.map(function(d) { return d.y; }))])
    .range([0, height]);

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

svg.selectAll("rect")
    .data(histogram)
  .enter().append("rect")
    .attr("width", x.rangeBand())
    .attr("x", function(d) { return x(d.x); })
    .attr("y", function(d) { return height - y(d.y); })
    .attr("height", function(d) { return y(d.y); });

svg.append("line")
    .attr("x1", 0)
    .attr("x2", width)
    .attr("y1", height)
    .attr("y2", height);

</script> 

히스토그램 요점의 전체 예는 다음과 같습니다. https://gist.github.com/993912


5
나는 쉬울이 대답하는 방법을 발견 stackoverflow.com/a/11948988/511203
마이크 고르 스키에게

100 % 및 (DIV 등) DOM 용기 크기에 적용 제가있는 간단한 방법은 SVG 폭과 높이를 만드는 것이다 stackoverflow.com/questions/47076163/...
MTX

답변:


316

이 그래프를 다시 그릴 필요로하지 않는이 작업을 수행하는 또 다른 방법은, 그리고 그것을 수정 관련 뷰 박스preserveAspectRatio가 온 속성 <svg>요소를 :

<svg id="chart" width="960" height="500"
  viewBox="0 0 960 500"
  preserveAspectRatio="xMidYMid meet">
</svg>

11/24/15 업데이트 : 대부분의 최신 브라우저는 에서 SVG 요소 의 가로 세로 비율유추viewBox 할 수 있으므로 차트 크기를 최신 상태로 유지할 필요가 없습니다. 이전 브라우저를 지원해야하는 경우 창의 크기가 다음과 같이 조정될 때 요소의 크기를 조정할 수 있습니다.

var aspect = width / height,
    chart = d3.select('#chart');
d3.select(window)
  .on("resize", function() {
    var targetWidth = chart.node().getBoundingClientRect().width;
    chart.attr("width", targetWidth);
    chart.attr("height", targetWidth / aspect);
  });

그리고 svg 내용은 자동으로 조정됩니다. 여기에 약간의 수정 사항이있는 실제 작동 예를 볼 수 있습니다 . 창 또는 오른쪽 하단 창의 크기를 조정하여 반응 방식을 확인하십시오.


1
나는이 접근법 shawn, 2 질문을 정말로 좋아합니다. 1보기 상자가 크로스 브라우저에서 작동합니까? 2. 귀하의 예에서 원의 크기는 조정되지만 창에 맞지 ​​않습니다. svg보기 상자 또는 종횡비에 문제가 있습니까?
매트 알콕

4
위의 방법이 올바르게 작동하는지 잘 모르는이 방법을 사용하십시오. Matt이 말했듯이 원의 크기는 조정되지만 그래프의 왼쪽에서 첫 번째 원까지의 거리는 원과 동일한 속도로 조정되지 않습니다. jsfiddle에서 공정한 부분을 찾으려고했지만 Chrome을 사용하여 예상대로 작동하지 못했습니다. viewBox 및 preserveAspectRatio는 어떤 브라우저와 호환됩니까?
Chris Withers

9
사실 부모의 100 % 폭 높이 설정과 SVG에 불과 뷰 박스와 preserveAspectRatio을 설정하고 부모가 부트 스트랩 / 플렉스 그리드있는 것은 크기 조정 이벤트 처리없이 나를 위해 작동
딥우

2
Deepu가 올바른지 확인합니다. 부트 스트랩 / 플렉스 그리드와 함께 작동합니다. 감사합니다 @Deepu.
Mike O'Connor

3
참고 : 여러 d3 자습서에서 1.) html에서 svg 너비와 높이가 설정되고 2) 시각화는 onload 함수 호출을 통해 이루어집니다 . SVG 너비와 높이가 실제 HTML로 설정되어있는 경우 위의 기술을 사용, 이미지가 확장되지 않습니다.
SumNeuron

34

'반응 형 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://demosthenes.info/blog/744/Make-SVG-Responsive

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


1
필자의 경우 패딩-하단 : 100 %는 컨테이너가 맨 아래에 여분의 패딩을 갖도록하여 제거하기 위해 50 %로 변경했습니다. 코드를 사용하고 svg를 성공적으로 응답했습니다. 감사합니다.
V-SHY

20

이 문제를 해결하기 위해 작은 요점을 코딩했습니다.

일반적인 솔루션 패턴은 다음과 같습니다.

  1. 스크립트를 계산 및 그리기 기능으로 나눕니다.
  2. 그리기 기능이 동적으로 그려지고 시각화 너비 및 높이 변수로 구동되는지 확인하십시오 (이를 수행하는 가장 좋은 방법은 d3.scale api를 사용하는 것입니다).
  3. 마크 업의 참조 요소에 도면을 바인딩 / 연결합니다. (이에 jquery를 사용했기 때문에 가져 왔습니다).
  4. 이미 그려져 있으면 제거하십시오. jquery를 사용하여 참조 된 요소에서 치수를 가져옵니다.
  5. 그리기 기능을 창 크기 조정 기능에 바인딩 / 체인합니다. 타임 아웃 이후에만 다시 그리기 위해이 체인에 디 바운스 (타임 아웃)를 도입하십시오.

또한 속도를 위해 축소 된 d3.js 스크립트를 추가했습니다. 요점은 여기에 있습니다 : https://gist.github.com/2414111

jquery 참조 백 코드 :

$(reference).empty()
var width = $(reference).width();

디 바운스 코드 :

var debounce = function(fn, timeout) 
{
  var timeoutID = -1;
  return function() {
     if (timeoutID > -1) {
        window.clearTimeout(timeoutID);
     }
   timeoutID = window.setTimeout(fn, timeout);
  }
};

var debounced_draw = debounce(function() {
    draw_histogram(div_name, pos_data, neg_data);
  }, 125);

 $(window).resize(debounced_draw);

즐겨!


작동하지 않는 것 같습니다. 수정 된 요지에 포함 된 html 파일의 창 크기를 조정하면 창이 작아 질 때 막대 그래프가 잘
립니다

1
SVG는 벡터 형식입니다. 가상 뷰 박스 크기를 설정 한 다음 실제 크기로 조정할 수 있습니다. JS를 사용할 필요가 없습니다.
phil pirozhkov

4

ViewBox를 사용하지 않고

다음은 다음을 사용하지 않는 솔루션의 예입니다 viewBox.

열쇠는의 범위 를 업데이트하는 것 입니다 데이터를 배치하는 데 사용되는 스케일 입니다.

먼저 원래 종횡비를 계산하십시오.

var ratio = width / height;

그런 다음 각 크기 조정에 업데이트 rangex과를 y:

function resize() {
  x.rangeRoundBands([0, window.innerWidth]);
  y.range([0, window.innerWidth / ratio]);
  svg.attr("height", window.innerHeight);
}

높이는 너비와 종횡비를 기준으로하여 원래 비율이 유지됩니다.

마지막으로, 차트를 "다시 그리기"– x또는 y스케일 중 하나에 의존하는 모든 속성을 업데이트하십시오 .

function redraw() {
    rects.attr("width", x.rangeBand())
      .attr("x", function(d) { return x(d.x); })
      .attr("y", function(d) { return y.range()[1] - y(d.y); })
      .attr("height", function(d) { return y(d.y); });
}

주에 다시 크기 조정 것을 rects당신은의 상한선 사용할 수 range의를 y명시 적으로 높이를 사용하는 대신, :

.attr("y", function(d) { return y.range()[1] - y(d.y); })


3

c3.js를 통해 d3.js를 사용 하는 경우 응답 성 문제에 대한 솔루션은 매우 간단합니다.

var chart = c3.generate({bindTo:"#chart",...});
chart.resize($("#chart").width(),$("#chart").height());

생성 된 HTML은 다음과 같습니다.

<div id="chart">
    <svg>...</svg>
</div>

2

Shawn Allen의 답변은 훌륭했습니다. 그러나 매번이 작업을 수행하지 않을 수도 있습니다. vida.io 에서 호스팅하면 svg 시각화에 자동 응답됩니다.

이 간단한 임베드 코드를 사용하여 반응 형 iframe을 얻을 수 있습니다.

<div id="vida-embed">
<iframe src="http://embed.vida.io/documents/9Pst6wmB83BgRZXgx" width="auto" height="525" seamless frameBorder="0" scrolling="no"></iframe>
</div>

#vida-embed iframe {
  position: absolute;
  top:0;
  left: 0;
  width: 100%;
  height: 100%;
}

http://jsfiddle.net/dnprock/npxp3v9d/1/

공개 : 나는이 기능을 vida.io에 빌드합니다 .


2

plottable.js 와 같은 d3 래퍼를 사용하는 경우 가장 쉬운 해결책은 이벤트 리스너를 추가 한 다음 다시 그리기 함수 ( plottable.js에서) 를 호출하는 것 입니다. plottable.js의 경우 이것은 훌륭하게 작동합니다 (이 접근법은 잘 문서화되어 있지 않습니다).redraw

    window.addEventListener("resize", function() {
      table.redraw();
    });

가능한 한 빨리 다시 그리기를 피하기 위해이 함수를 설명하는 것이 가장 좋습니다.
Ian

1

사람들이 여전히이 질문을 방문하는 경우-다음은 나를 위해 일한 것입니다.

  • iframe을 div로 묶고 CSS를 사용하여 해당 div에 40 %의 패딩을 추가하십시오 (원하는 종횡비에 따른 비율). 그런 다음 iframe 자체의 너비와 높이를 모두 100 %로 설정하십시오.

  • iframe에로드 할 차트가 포함 된 html 문서에서 너비를 svg가 추가 된 div의 너비 (또는 본문 너비)로 설정하고 높이 대 너비 * 종횡비를 설정하십시오.

  • 사람들이 휴대 전화를 회전 할 때 차트 크기를 조정할 수 있도록 창 크기를 조정할 때 iframe 컨텐츠를 다시로드하는 함수를 작성하십시오.

내 웹 사이트의 예 : http://dirkmjk.nl/en/2016/05/embedding-d3js-charts-responsive-website

업데이트 2016 년 12 월 30 일

위에서 설명한 접근 방식에는 몇 가지 단점이 있습니다. 특히 D3에서 만든 svg의 일부가 아닌 제목 및 캡션을 고려하지 않는 경우가 많습니다. 나는 더 나은 접근법이라고 생각하는 것을 보았습니다.

  • D3 차트의 너비를 연결된 div의 너비로 설정하고 가로 세로 비율을 사용하여 높이를 적절하게 설정하십시오.
  • 임베드 된 페이지가 HTML5의 postMessage를 사용하여 높이와 URL을 상위 페이지로 보내도록하십시오.
  • 상위 페이지에서 url을 사용하여 해당 iframe을 식별하고 (페이지에 둘 이상의 iframe이있는 경우 유용함) 높이를 포함 된 페이지의 높이로 업데이트하십시오.

내 웹 사이트의 예 : http://dirkmjk.nl/en/2016/12/embedding-d3js-charts-responsive-website-better-solution


0

D3 데이터 조인의 기본 원칙 중 하나는 dem 등원이라는 것입니다. 즉, 동일한 데이터로 데이터 조인을 반복적으로 평가하면 렌더링 된 출력이 동일합니다. 따라서 입력, 업데이트 및 종료 선택에주의를 기울여 차트를 올바르게 렌더링하는 한, 크기가 변경 될 때해야 할 일은 차트 전체를 다시 렌더링하는 것입니다.

당신이해야 할 다른 두 가지가 있습니다. 하나는 창 크기 조정 핸들러를 조절하여 조절합니다. 또한, 폭 / 높이를 하드 코딩하는 대신 포함 요소를 측정하여 달성해야합니다.

대안으로, 다음은 데이터 결합을 올바르게 처리하는 D3 구성 요소 세트 인 d3fc를 사용하여 렌더링 된 차트 입니다. 또한 반응 형 차트를 쉽게 만들 수있는 요소가 포함 된 측정 값을 측정하는 데카르트 차트가 있습니다.

// create some test data
var data = d3.range(50).map(function(d) {
  return {
    x: d / 4,
    y: Math.sin(d / 4),
    z: Math.cos(d / 4) * 0.7
  };
});

var yExtent = fc.extentLinear()
  .accessors([
    function(d) { return d.y; },
    function(d) { return d.z; }
  ])
  .pad([0.4, 0.4])
  .padUnit('domain');

var xExtent = fc.extentLinear()
  .accessors([function(d) { return d.x; }]);

// create a chart
var chart = fc.chartSvgCartesian(
    d3.scaleLinear(),
    d3.scaleLinear())
  .yDomain(yExtent(data))
  .yLabel('Sine / Cosine')
  .yOrient('left')
  .xDomain(xExtent(data))
  .xLabel('Value')
  .chartLabel('Sine/Cosine Line/Area Chart');

// create a pair of series and some gridlines
var sinLine = fc.seriesSvgLine()
  .crossValue(function(d) { return d.x; })
  .mainValue(function(d) { return d.y; })
  .decorate(function(selection) {
    selection.enter()
      .style('stroke', 'purple');
  });

var cosLine = fc.seriesSvgArea()
  .crossValue(function(d) { return d.x; })
  .mainValue(function(d) { return d.z; })
  .decorate(function(selection) {
    selection.enter()
      .style('fill', 'lightgreen')
      .style('fill-opacity', 0.5);
  });

var gridlines = fc.annotationSvgGridline();

// combine using a multi-series
var multi = fc.seriesSvgMulti()
  .series([gridlines, sinLine, cosLine]);

chart.plotArea(multi);

// render
d3.select('#simple-chart')
  .datum(data)
  .call(chart);

이 코드 펜에서 실제로 작동하는 것을 볼 수 있습니다.

https://codepen.io/ColinEberhardt/pen/dOBvOy

창 크기를 조정하고 차트가 올바르게 다시 렌더링되는지 확인할 수 있습니다.

전체 공개로서, 나는 d3fc의 관리자 중 한 명입니다.


답변을 최신 상태로 유지해 주셔서 감사합니다! 귀하의 게시물 중 일부에 대한 최근 업데이트를 확인한 결과 사람들이 자신의 콘텐츠를 다시 방문해 주셔서 감사합니다. 그러나 D3 v4 에서 편집하기 때문에 OP는 v3을 명확하게 참조하는 반면 향후 독자에게는 혼란을 줄 수 있으므로 주의해서이 개정판은 더 이상 문제에 완전히 맞지 않습니다 . 개인적으로, 내 자신의 대답 을 위해 원래 v3 섹션 을 계속 유지하면서 v4 섹션 을 추가하는 경향이 있습니다 . v4 는 두 게시물에 상호 참조를 추가하는 자체 답변의 가치가 있다고 생각 합니다. 하지만 그 ... 그냥 내 두 센트
고적운

정말 좋은 지적입니다! 쉽게 벗어날 수 있고 미래에 초점을 맞추고 새로운 기능을 제공합니다. 최신 d3 질문으로 판단하면 많은 사람들이 여전히 v3을 사용하고 있다고 생각합니다. 또한 미묘한 차이가 얼마나 미묘한 지 고민하고 있습니다! 내 편집을 :-) 방문거야
ColinE

0

전염병과 같은 크기 조정 / 틱 솔루션은 비효율적이며 앱에서 문제를 일으킬 수 있으므로 (예 : 툴팁은 창 크기 조정에 표시되는 위치를 다시 계산 한 다음 잠시 후에 차트 크기가 조정되고 페이지 크기가 조정됩니다) 레이아웃과 도구 설명이 다시 잘못되었습니다).

IE11과 같이 제대로 지원하지 않는 일부 구형 브라우저에서는 <canvas>측면을 유지 하는 요소를 사용 하여이 동작을 시뮬레이션 할 수 있습니다 .

16 : 9의 측면 인 960x540이 제공됩니다.

<div style="position: relative">
  <canvas width="16" height="9" style="width: 100%"></canvas>
  <svg viewBox="0 0 960 540" preserveAspectRatio="xMidYMid meet" style="position: absolute; top: 0; right: 0; bottom: 0; left: 0; -webkit-tap-highlight-color: transparent;">
  </svg>
</div>

0

여기에 복잡한 답변이 많이 있습니다.

기본적으로 당신이해야 할 일은 속성을 선호하는 속성 widthheight속성을 버리는 것입니다 viewBox.

width = 500;
height = 500;

const svg = d3
  .select("#chart")
  .append("svg")
  .attr("viewBox", `0 0 ${width} ${height}`)

여백이있는 경우 너비 / 높이에 여백을 추가 한 다음 그다음을 추가하고 g평상시처럼 변환 할 수 있습니다.


-1

부트 스트랩 3 을 사용 하여 시각화의 크기를 조정할 수도 있습니다 . 예를 들어 HTML 코드를 다음과 같이 설정할 수 있습니다 .

<div class="container>
<div class="row">

<div class='col-sm-6 col-md-4' id="month-view" style="height:345px;">
<div id ="responsivetext">Something to write</div>
</div>

</div>
</div>

내 요구 때문에 고정 높이를 설정했지만 크기를 자동으로 남겨 둘 수도 있습니다. "col-sm-6 col-md-4"는 div가 다른 장치에 반응하도록합니다. http://getbootstrap.com/css/#grid-example-basic 에서 자세한 내용을 확인할 수 있습니다.

id month-view 의 도움으로 그래프에 액세스 할 수 있습니다 .

d3 코드에 대해서는 자세하게 설명하지 않겠습니다. 다른 화면 크기에 적응하는 데 필요한 부분 만 입력하겠습니다.

var width = document.getElementById('month-view').offsetWidth;

var height = document.getElementById('month-view').offsetHeight - document.getElementById('responsivetext2').offsetHeight;

너비는 id month-view로 div의 너비를 가져와 설정됩니다.

내 경우의 높이에는 전체 면적이 포함되어서는 안됩니다. 또한 막대 위에 텍스트가 있으므로 해당 영역을 계산해야합니다. 그렇기 때문에 텍스트의 영역을 ID 응답 텍스트로 식별했습니다. 막대의 허용 높이를 계산하기 위해 div 높이에서 텍스트 높이를 뺍니다.

이를 통해 모든 다른 화면 / div 크기를 채택 할 수있는 막대를 가질 수 있습니다. 최선의 방법은 아니지만 내 프로젝트의 요구에 반드시 부합합니다.


1
안녕하세요, 이것을 시도했지만 달력이나 차트와 같은 svg 내용의 크기가 조정되지 않았습니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.