d3.jsでウィンドウのサイズが変更されたときにsvgのサイズを変更する


183

私はd3.jsで散布図を描画しています。この質問の助けを借りて:
画面、現在のWebページ、ブラウザウィンドウのサイズを取得します

私はこの答えを使用しています:

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

ここで、ユーザーがウィンドウのサイズを変更するときに、何かがプロットのサイズ変更を処理するようにしたいと思います。

PS:コードで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プロットの関連する高さと幅に設定する必要があります:.attr("viewBox","0 0 " + width + " " + height) 場所widthheight他の場所で定義されます。つまり、SVGをビューボックスでクリップしたくない場合です。padding-bottomcss のパラメーターを更新して、svg要素のアスペクト比に一致させることもできます。優れた応答も-非常に役立つ、と御馳走を働いています。ありがとう!
Andrew Guy

13
同じ考えに基づいて、アスペクト比を維持することを含まない答えを出すのは興味深いでしょう。問題は、サイズ変更時にウィンドウ全体をカバーし続けるsvg要素を持つことでした。これは、ほとんどの場合、異なるアスペクト比を意味します。
Nicolas Le Thierry d'Ennequin 2016

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 2016

1
@EamonnKenny私はあなたにこれ以上同意することはできません。7か月後のもう1つの回答の方が優れています:)
slf

このメソッドの場合: "d3.select(window).on" "d3.select(window).off"または "d3.select(window).unbind"
sea-kg


この答えは決して劣るものではありません。彼らの答えは、グラフのすべての側面(目盛り、軸、線、テキスト注釈を含む)をスケーリングし、画面サイズによっては見苦しく、他の画面では悪く、極端なサイズでは判読できない場合があります。この方法(または、より新しいソリューションの場合は@Angu Agarwal)を使用してサイズを変更する方が良いと思います。
ルナー・バーグ

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要素が追加されます。


すべてのウィンドウサイズ変更イベントで要素全体を再描画している場合、これによりパフォーマンスが完全に
失わ

2
しかし、これは正しいです。レンダリングするときに、軸をはめ込むかどうか、凡例を非表示にするかどうか、使用可能なコンテンツに基づいて他のことを行うかどうかを決定できます。純粋にCSS /ビューボックスソリューションを使用すると、視覚化が押しつぶされて見えるようになります。画像には適していますが、データには適していません。
Chris Knoll

8

強制レイアウトでは、単に 'height'および 'width'属性を設定しても、プロットを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 '19年

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.