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>
完全なヒストグラムGistの例: https://Gist.github.com/993912
グラフの再描画を必要としない別の方法があり、<svg>
要素の viewBox および preserveAspectRatio 属性を変更する必要があります。
<svg id="chart" width="960" height="500"
viewBox="0 0 960 500"
preserveAspectRatio="xMidYMid meet">
</svg>
Update 11/24/15:最新のブラウザのほとんどは アスペクト比を推測viewBox
からのSVG要素のチャートのサイズを最新に保つ必要はありません。古いブラウザをサポートする必要がある場合は、ウィンドウのサイズが次のように変更されたときに要素のサイズを変更できます。
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の内容は自動的にスケーリングされます。これの実際の例を見ることができます(いくつかの変更を加えて) here :ウィンドウまたは右下のペインのサイズを変更して、どのように反応するかを確認します。
「レスポンシブ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
これを解決するために小さな要点をコーディングしました。
一般的なソリューションパターンは次のとおりです。
速度を上げるために、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
の使用に依存しないソリューションの例です。
重要なのは、データの配置に使用されるscalesのrangeを更新することです。
最初に、元のアスペクト比を計算します。
var ratio = width / height;
次に、サイズ変更のたびに、range
およびx
の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>
plottable.js のようなd3 wrapperを使用している場合、解決策は、イベントリスナーを追加してから、redraw関数( plottable.jsのredraw
)。 plottable.jsの場合、これは非常にうまく機能します(このアプローチの文書化は不十分です)。
window.addEventListener("resize", function() {
table.redraw();
});
ショーン・アレンの答えは素晴らしかった。ただし、毎回これを行う必要はありません。ホストを 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 でこの機能を構築します。
人々がまだこの質問を訪問している場合-私のために働いたのは次のとおりです:
Iframeをdivで囲み、cssを使用して、そのdivにたとえば40%のパディングを追加します(パーセンテージは希望するアスペクト比に依存します)。次に、iframe自体の幅と高さの両方を100%に設定します。
Iframeに読み込まれるチャートを含むHTMLドキュメントで、svgが追加されるdivの幅(または本文の幅)にwidthを設定し、高さをwidth *アスペクト比に設定します。
ウィンドウのサイズ変更時にiframeコンテンツをリロードする関数を作成し、人々が携帯電話を回転させたときにチャートのサイズを調整します。
私のウェブサイトの例: http://dirkmjk.nl/en/2016/05/embedding-d3js-charts-responsive-website
2016年12月30日更新
上記のアプローチにはいくつかの欠点があります。特に、D3で作成されたsvgの一部ではないタイトルとキャプションを考慮に入れていないという点です。それ以来、私はより良いアプローチだと思うことに出会いました:
私のウェブサイトの例: http://dirkmjk.nl/en/2016/12/embedding-d3js-charts-responsive-website-better-solution
D3データ結合の基本原則の1つは、それがべき等であることです。つまり、同じデータでデータ結合を繰り返し評価すると、レンダリングされた出力は同じになります。したがって、チャートを正しくレンダリングする限り、選択、更新、終了の選択に注意してください。サイズが変更されたときに必要なのは、チャート全体を再レンダリングすることだけです。
他にもやるべきことがいくつかあります。1つは、ウィンドウのサイズ変更ハンドラーをデバウンスしてスロットルを調整することです。また、幅/高さをハードコーディングするのではなく、これを含む要素を測定することでこれを実現する必要があります。
別の方法として、データ結合を正しく処理する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>