D3.jsで棒グラフを広げるUIを作ってみた。
棒グラフをクリックしたら、画面いっぱいまで広がって詳細を表示するというUIを作ってみました。
詳細情報を沢山表示したい場合には便利かもしれません。
D3を使えば簡単に作れるかな?と思っていたのですが、SVGの表示順の罠にはまって結構面倒なことに。
SVGにもz-indexが欲しくなりました。
表示順の罠
SVGでは要素の順番で重なり方が決まります。
下記の例では、赤いcircle要素の後に青いcircle要素が設置されているため、青い円が赤い円の上に重なって表示されています。
1 2 3 4 |
<svg width="300" height="100"> <circle cx="100" cy="50" r="50" fill="red" /> <circle cx="150" cy="50" r="50" fill="blue" /> </svg> |
SVG要素には「z-index」が効かないため、要素の重なりを変更したい場合は要素の並びを入れ替えるしか方法がありません。これが結構面倒です。
ちょっとだけ解説
全コードはbl.ocks.orgの方で公開しているので、ハマリポイントだけ説明します。
初めにg要素(グループ化)を使って3層作っておくことで前述の重なり問題を回避しています。
id=stageの層は棒グラフを表示する層、id=overlayの層はクリックされた際のトランジションを行う層、id=info層はオーバーレイの上に詳細情報等を出力する層として設置しています。
1 2 3 4 5 |
<svg> <g id="stage"></g> <g id="overlay"></g> <g id="info"></g> </svg> |
下記は、棒グラフがクリックされた際の処理です。
一見複雑なことをやっているように見えなくもないですが、実際には単純な処理です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
barChart.on('click', function(d, i){ //棒グラフのコピーを作る var copryRect = overlay.append('rect') .on('click', function(){ //コピーがクリックされた際の処理(閉じる) label.text("").style('opacity', 0); description.text("").style('opacity', 0); copryRect.transition().attr({ 'x': (i * 100) + margin.left, 'y': h - yScale(d.value), 'height':yScale(d.value), 'width':50, }) .call(endall, function(){ copryRect.remove(); }) }); //オリジナルからattributesを取得し、コピーに適用する Array.prototype.slice.call(d3.select(this).node().attributes).forEach(function(d){ copryRect.attr(d.name, d.value) }); //コピーを画面いっぱいまで広げる copryRect.transition().attr({ x:0, y:0, width:w, height:h }) .call(endall, function(){ label.text(d.label).transition().style('opacity', 1); description.text(d.description).transition().style('opacity', 1); }); }); |
棒グラフがクリックされると、まずoverlay層にクリックされた棒グラフ(rect)要素のコピーを作ります。
その後、コピーを画面いっぱいにまで広げ、info層に詳細情報を出力しています。
コピー自身がクリックされると、元のステータスまでトランジションしてからremoveされて消えます。
当初は、クリックされた棒グラフ(rect)要素自身を画面いっぱいにまで広げるつもりだったのですが、それだと他の棒グラフの要素が上位に表示されてしまう問題があったため、コピーを作成する方法で回避しました。