Programing

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

lottogame 2020. 4. 26. 20:59
반응형

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


960500 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


이 그래프를 다시 그릴 필요로하지 않는이 작업을 수행하는 또 다른 방법은, 그리고 그것을 수정 관련 뷰 박스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 내용은 자동으로 조정됩니다. 여기에 대한 실제 예제를 볼 수 있습니다 (몇 가지 수정 사항이 있음) . 창 또는 오른쪽 하단 창의 크기를 조정하여 반응 방식을 확인하십시오.


'반응 형 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. 스크립트를 계산 및 그리기 기능으로 나눕니다.
  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);

즐겨!


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

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 ratio = width / height;

var x = d3.scale.ordinal()
  .domain(histogram.map(function(d) {
    return d.x;
  }))

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

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

var rects = svg.selectAll("rect").data(histogram);
rects.enter().append("rect");

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

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

d3.select(window).on('resize', function() {
  resize();
  redraw();
})

resize();
redraw();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>


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

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

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

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

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에 빌드합니다 .


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

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

사람들이 여전히이 질문을 방문하는 경우-여기 나를 위해 일한 것이 있습니다 :

  • 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


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의 관리자 중 한 명입니다.


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

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>

부트 스트랩 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 크기를 채택 할 수있는 막대를 가질 수 있습니다. 최선의 방법은 아니지만 내 프로젝트의 요구에 반드시 부합합니다.

참고 URL : https://stackoverflow.com/questions/9400615/whats-the-best-way-to-make-a-d3-js-visualisation-layout-responsive

반응형