質問

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

役に立ちましたか?

解決

グラフを再描画する必要がないこれを行う別の方法があり、それは ビューボックス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. 描画関数をウィンドウのサイズ変更関数にバインド/チェーンします。このチェーンにデバウンス(タイムアウト)を導入して、タイムアウト後にのみ再描画するようにします。

また、速度のためにMinified 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;

次に、サイズ変更ごとに、更新します rangexy:

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

高さは幅とアスペクト比に基づいているため、元の割合が維持されることに注意してください。

最後に、チャートを「redRaw」 - どちらかに依存する属性を更新します 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 の上限を使用できます rangey, 、高さを明示的に使用するのではなく、

.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>

d3.jsを使用している場合 c3.js 応答性の問題の解決策は非常に簡単です。

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

生成されたHTMLが次のように見える場所:

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

ショーン・アレンの答えは素晴らしかった。しかし、あなたは毎回これをしたくないかもしれません。ホストする場合 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.

あなたが使用している場合 D3ラッパー お気に入り plottable.js, 、最も簡単な解決策があるかもしれないことに注意してください イベントリスナーの追加 そして、aを呼びます 再描画 関数 (redraw plottable.js)。 plottable.jsの場合、これはうまく機能します(このアプローチの文書化が不十分です):

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

人々がまだこの質問を訪れている場合に備えて、ここに私のために働いたものがあります:

  • IFRAMEをDIVに囲み、CSSを使用して、たとえばそのdivに40%のパディングを追加します(必要なアスペクト比に依存する割合)。次に、iframe自体の幅と高さの両方を100%に設定します。

  • iframeにロードされるチャートを含むHTMLドキュメントでは、SVGが追加される(または身体の幅に)widthに幅を設定し、高さを幅に *アスペクト比に設定します。

  • 携帯電話を回転させるときにチャートのサイズを適応させるために、ウィンドウのサイズ変更時にiframeコンテンツをリロードする関数を書きます。

私のウェブサイトの例:http://dirkmjk.nl/en/2016/05/embedding-d3js-charts-responsive-website

2016年12月30日更新

上で説明したアプローチにはいくつかの欠点があります。特に、D3が作成したSVGの一部ではないタイトルとキャプションを考慮して高さを考慮していないことです。それ以来、私はもっと良いアプローチだと思うことに出くわしました:

  • D3チャートの幅を取り付けられているDivの幅に設定し、アスペクト比を使用してそれに応じて高さを設定します。
  • 埋め込みページに、HTML5のポストメサージを使用して、その高さとURLを親ページに送信します。
  • 親ページで、URLを使用して、対応するiframe(ページに複数のiframeがある場合に便利)を識別し、埋め込みページの高さまでその高さを更新します。

私のウェブサイトの例: http://dirkmjk.nl/en/2016/12/embedding-d3js-charts-responsive-website-better-solution

D3データ結合の基本原則の1つは、それがiDempotentであることです。言い換えれば、同じデータでデータ結合を繰り返し評価する場合、レンダリングされた出力は同じです。したがって、チャートを正しくレンダリングしている限り、Enter、更新、終了の選択に注意してください。サイズが変更されたときに必要なことは、チャート全体を再レンダリングするだけです。

他にすべきことがいくつかあります。1つは、スロットルするためにウィンドウサイズのハンドラーを削除することです。また、ハードコーディングの幅 /高さではなく、これは含有要素を測定することで実現する必要があります。

別の方法として、ここにあなたのチャートが使用されています D3FC, 、これは、データ参加を正しく処理するD3コンポーネントのセットです。また、「レスポンシブな」チャートを簡単に作成できる要素を含むITを測定するデカルトチャートもあります。

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

このCodepenで動作しているのを見ることができます:

https://codepen.io/colineberhardt/pen/dobvoy

ウィンドウをサイズ変更して、チャートが正しく再レンダリングされていることを確認できます。

完全な開示として、私はD3FCのメンテナーの1人です。

Plagueのようなサイズ変更/ティックソリューションは、それらが非効率的でアプリに問題を引き起こす可能性があるため、Plagueのようなティックソリューションを避けます(たとえば、ツールチップがウィンドウのサイズ変更に表示される位置を再計算し、その後、チャートも変更し、ページは再び変更されます。レイアウトとこれで、ツールチップが再び間違っています)。

この動作は、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の助けを借りてグラフにアクセスできます 月視点.

D3コードについて詳しく説明することはありません。さまざまな画面サイズに適応するために必要な部分のみを入力します。

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

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

幅は、ID Month-ViewでDIVの幅を取得することにより設定されます。

私の場合の高さには、エリア全体を含めるべきではありません。また、バーの上にいくつかのテキストがあるので、その領域も計算する必要があります。そのため、ID escressivetextを使用してテキストの領域を特定しました。バーの許可された高さを計算するために、divの高さからテキストの高さを差し引きました。

これにより、すべての異なる画面/Divサイズを採用するバーを持つことができます。それはそれを行うための最良の方法ではないかもしれませんが、それは私のプロジェクトのニーズのために確かに機能します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top