GMOモバイルのT.Oです。
管理画面などで売上などのデータをグラフ表示したいという要望は多いのではないでしょうか?
そこでこの記事では以下のような棒グラフの実装手順をお伝えします。
グラフ描画にはJavaScriptライブラリであるD3.jsを利用します。
1 2 3 4 5 6 7 8 9 |
var json_data = [ {"md": "11/14", "sales": "14,352"}, {"md": "11/15", "sales": "25,746"}, {"md": "11/16", "sales": "35,383"}, {"md": "11/17", "sales": "63,822"}, {"md": "11/18", "sales": "54,399"}, {"md": "11/19", "sales": "63,746"}, {"md": "11/20", "sales": "73,292"} ] |
2.グラフデータを描画するHTMLの用意
SVG要素を記述したHTMLを記述します。D3.jsで生成するグラフはSVG要素の部分に展開されることになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<div class="chart"> <table class="graph_legends"> <tbody> <tr> <td style="width: 40%;"></td> <td style="width: 20%;"> <div class="imp"></div> 売上高</td> <td style="width: 40%;"></td> </tr> </tbody> </table> </div> |
2.グラフデータ、表示する図形の設定
棒グラフを表示するのにSVGの矩形(rect)を使用します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
d3.select("#barchart") // SVG要素を指定 .selectAll("rect") // 書き換え・追加対象となる描画要素を選択 .data(json_data) // グラフデータを設定 .enter() // 設定されたグラフデータに対応した新しいノードを生成 .append("rect") // SVGの四角形を生成 .attr("x", function(d, i) { // グラフデータの要素ごとに描画開始点のX座標を指定 return offset_x + i * (w / json_data.length); }) .attr("width", 30) // 矩形の幅を指定 .attr("y", function(d, i) { // グラフデータの要素ごとに描画開始点のY座標を指定 return h + offset_y_top - scaleYBarChart(parseInt(d.sales,10)); }) .attr("height", function(d, i) { // 矩形の高さを指定 return scaleYBarChart(parseInt(d.sales,10)); }) |
3.アニメーションを設定する
棒グラフが下から延びるような表示にします。
transitionメソッドなどを追加し、変化させたい属性の初期値と最終値を指定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
d3.select("#barchart") // SVG要素を指定 .selectAll("rect") // 書き換え・追加対象となる描画要素を選択 .data(json_data) // グラフデータを設定 .enter() // 設定されたグラフデータに対応した新しいノードを生成 .append("rect") // SVGの四角形を生成 .attr("x", function(d, i) { // グラフデータの要素ごとに描画開始点のX座標を指定 return offset_x + i * (w / json_data.length); }) .attr("width", 30) // 矩形の幅を指定 .attr("y", h + offset_y_top) // Y座標の初期値 .attr("height", 0) // 矩形の高さの初期値 .transition() // アニメーション指定 .duration(300) // アニメーション時間(msec単位) .delay(function(d, i){ // 指定した時間(msec単位)描画待ち return i * 100; }) .attr("y", function(d, i) { // Y座標の最終値 return h + offset_y_top - scaleYBarChart(parseInt(d.sales,10)); }) .attr("height", function(d, i) { // 矩形の高さの最終値 return scaleYBarChart(parseInt(d.sales,10)); }) |
4.グラフを完成させる
日付表示、凡例、スケール表示、マウスオーバーした時のツールチップなどのグラフ要素を追加して完成です。
D3.jsの場合、グラフデータに直接ひもづく要素を描画するのは容易なのですが、それ以外、例えば凡例などの要素は扱いにくいため、別途HTML要素として記述する方がお手軽です。
またグラフは期間中の傾向をざっくり把握することができるような表示内容にとどめ、詳細な数値などは表形式でグラフの下に表示するのがおすすめです。
D3.jsはもっと複雑なグラフや図形を描画するのも利用されています。ご興味のある方は以下のサイトで多くの事例が紹介されていますので是非参照してみてください。
D3.js – 日本語ドキュメント
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
<!-- javascript --> <script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script> <style type="text/css"> <!-- div.chart { width:500px; height:230px; border:1px solid gray; margin-left:auto; margin-right:auto; margin-bottom:3px; font-size: 10pt; } #barchart { width: 500px; height: 200px; } .graph_legends { table-layout: fixed; width: 500px; } .graph_legends div.imp { width:20px; height: 15px; background-color:green; float:left; margin-right:3px; } .tip { position: absolute; z-index: 9999; visibility : hidden; border: 1px solid black; background-color: yellow; width: 100px; height: 16px; overflow : hidden; text-align : left; font-size: 10pt; font-family : Tahoma, Optima, Helvetica; color: black; } --> </style> </pre> <div class="chart"> <table class="graph_legends"> <tbody> <tr> <td style="width: 40%;"></td> <td style="width: 20%;"> <div class="imp"></div> 売上高</td> <td style="width: 40%;"></td> </tr> </tbody> </table> </div> <script type="text/javascript"> var json_data = [ {"md": "11/14", "sales": "14,352"}, {"md": "11/15", "sales": "25,746"}, {"md": "11/16", "sales": "35,383"}, {"md": "11/17", "sales": "63,822"}, {"md": "11/18", "sales": "54,399"}, {"md": "11/19", "sales": "63,746"}, {"md": "11/20", "sales": "73,292"} ]; barChart(json_data, 'barchart'); function barChart(dataset, id){ var svg_element = document.getElementById(id); var svg_w = window.getComputedStyle(svg_element, null).getPropertyValue("width"); var svg_h = window.getComputedStyle(svg_element, null).getPropertyValue("height"); svg_w = parseFloat(svg_w); svg_h = parseFloat(svg_h); var offset_x = 10; var offset_y_top = 20; var offset_y_bottom = 15; // 棒グラフを表示する幅、高さを設定 var w = svg_w - 2*offset_x; var h = svg_h - offset_y_top - offset_y_bottom; var svg_id = "#" + id; var svg = d3.select(svg_id); // スケール(横線)の描画 var range_x = d3.range(0, 5, 1); var step_y = h / 4; var grid = svg.append("g"); grid.selectAll("line.x") .data(range_x) .enter() .append("line") .attr("class", "grid") .attr("stroke", "#888888") .attr("x1", offset_x) .attr("y1", function(d, i){ return svg_h - offset_y_bottom - step_y * i; }) .attr("x2", svg_w - offset_x) .attr("y2", function(d, i){ return svg_h - offset_y_bottom - step_y * i; }) // 棒グラフの描画 var scaleYBarChart = d3.scale.linear() .domain([0, d3.max(dataset, function(d,i) { return parseInt(d.sales, 10); })]) .range([0, h*0.8]); var barChartElements = svg .selectAll("rect") .data(dataset) barChartElements .enter() .append("rect") .attr("x", function(d, i) { return offset_x + i * (w / dataset.length); }) .attr("y", h + offset_y_top) .attr("height", 0) .attr("width", 30) .attr("fill", "green") .style("opacity", 0.85) // マウスオーバーしたときの描画処理 .on("mouseover", function(d, i){ d3.select(this) .style("fill", "red") var x = parseInt(offset_x + i * (w / dataset.length)); var y = h + offset_y_top - scaleYBarChart(parseInt(d.sales,10)); y = parseInt(y,10); var sales_str = (d.sales).toLocaleString(); tooltip .style("left", (d3.event.pageX+10) +"px") .style("top", (d3.event.pageY-40) + "px") .style("visibility", "visible") .html(" 売上高:¥" + sales_str + "円") }) // マウスアウトした時の描画処理 .on("mouseout", function(d){ d3.select(this) .style("fill", "green"); tooltip.style("visibility", "hidden") }) .transition() .duration(300) .delay(function(d, i){ return i * 100; }) .attr("y", function(d, i) { return h + offset_y_top - scaleYBarChart(parseInt(d.sales,10)); }) .attr("height", function(d, i) { return scaleYBarChart(parseInt(d.sales,10)); }) // マウスオーバーした時のツールチップ var tooltip = d3.select("body") .append("div") .attr("class", "tip") // X軸に表示する月/日 svg.selectAll("text") .data(dataset) .enter() .append("text") .text(function(d, i) { return d.md; }) .attr("x", function(d, i) { return offset_x + i * (w / dataset.length); }) .attr("y", function(d) { return h + offset_y_top + 12; }) .attr("font-size", "12px") .attr("fill", "black"); } </script> |