今回のエンジニアブログを担当する藤岡です。
宜しくお願い致します。
前回に引き続き、D3.jsを用いたデータの可視化を行っていきたいと思います。
今回は、D3.jsの機能を用いて、棒グラフにデータドリブンなアニメーションをさせてみます。
アニメーション機能が搭載されているグラフを見ると、楽しいですよね。
フロー
1. グラフを用意
2. 目盛りを追加
3. アニメーション
1. グラフを用意
ではまず、前回のようにサンプルデータセットを元にデータバインディングを行い、svg描画領域にグラフを作成します。
今回は、前回使用したグラフを少し拡張したものを使用します。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>D3.js Animation</title> <style> svg { border: 1px solid black; } #barGraph rect { stroke : rgb(0, 0, 130); stroke-width : 1px; fill : rgb(0, 0, 200); } </style> <script charset="utf-8" type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script> </head> <body> <h1>Sample Graph</h1> <div id="barGraph"></div> <script src="index.js"></script> </body> </html>
var svg_w = 550; // 幅 var svg_h = 300; // 高さ var dataSet = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]; // x軸のスケールを設定 // 横棒のスケールマッピングと軸の追加に使用する var xScale = d3.scale.linear() .domain([0, d3.max(dataSet)]) .range([0, svg_w]); var svg = d3.select("#barGraph") // barGraph要素を監視 .append("svg") // svg領域を追加 .attr({ width: svg_w, // 幅を設定 height: svg_h, // 高さを設定 }); // データに基づいて描画する svg.selectAll("rect") .data(dataSet) .enter() .append("rect") .attr("x", 0) .attr("y", function(d, i) { return i * 25; }) .attr("height", "20px") .attr("width", function(d, i) { return xScale(d); });
前回同様にサンプルデータセットをバインディングし、
スケールによるマッピングを行い、数値を可視化しています。
実行すると、棒線が表示されます。
目盛りも無い状態なので、これではグラフであるとは言えません。
2. 目盛りを追加
そもそもグラフとして成り立たせるためには目盛りが必須です。
D3.jsでは、目盛りを設定する機能もバッチリ含まれています。
D3.jsで目盛りを表示するには、domainとrangeの設定が大切になってきます。
下記のソースコードを追加することで目盛りが追加されます。
.axis text { font-family: sans-serif; font-size: 15px; } .axis path, .axis line { fill: none; stroke: black; }
目盛りのスタイルを設定しています。
var xScale = d3.scale.linear() // リニアスケールを設定 .domain([0, d3.max(dataSet)]) // 元のデータ範囲 .range([0, svg_w]); // マッピングするサイズ
目盛りを生成する際にもD3.jsのスケール機能を使用します。
ドメインとレンジを設定し、目盛りの範囲を指定しています。
var xAxis = d3.svg.axis() .scale(xScale) .orient("bottom");
d3.svg.axis()が目盛りを処理するメソッドです。
上記のスケールを使用し、orient()によって目盛りの表示場所をbottomに指定しています。
目盛りの設定が完了したので、後は表示してあげるだけです。
svg.append("g") .attr("class", "axis") .attr("transform", "translate(0," + ((dataSet.length + 1) * 20 + 50) + ")") .call(xAxis);
目盛りを描画するにはsvg領域にg要素を追加します。
g要素を使うことで、g要素に内包されている複数の要素を1グループとして扱うことが出来るようになります。
transform属性で、translateによる並進を指定しています。今回は棒線の下に表示されるよう位置を調節しています。
call()に設定した目盛りを渡してやることで、目盛りが描画されます。
3. アニメーション
しかし、このままでは味気が全く無く、データドリブンであるとは言えません。
ということで、
・ボタンがクリックされたらデータを更新する
・データの更新時にアニメーションさせる
これら二つの機能を実装したいと思います。
◆ボタンクリック時のデータ更新
ボタンを追加するために、button要素を追加します。
<button id="update-btn">Update</button>
D3.jsではイベント処理を用意することが可能です。
要素に対して、on()メソッドを使ってイベント処理を設定します。
// ボタンクリック時の処理 d3.select("#update-btn").on("click", function() { var newDataSet = new Array(dataSet.length); for (var i = 0; i < newDataSet.length; i++) { newDataSet[i] = Math.floor(Math.random() * d3.max(dataSet)); } svg.selectAll("rect") .data(newDataSet) .attr("width", function(d, i) { return xScale(d); }); });
上記の処理は、update-btn要素がクリックされた時、ランダムに値を設定し直しています。
再設定されたデータセットを各要素にバインディングしてやることで、データの更新を行っています。
◆データ更新時にアニメーションさせる
アニメーションの実装ですが、実は凄く簡単です。
データを更新する前に、要素にtransition()メソッドを追加してあげるだけで実装することが出来ます。
棒線のwidthを設定していた箇所を以下のように変更します。
// 初期rect描画 svg.selectAll("rect") .data(dataSet) .enter() .append("rect") .attr("x", padding) .attr("y", function(d, i) { return i * 25 + 15; }) .attr("height", "20px") .attr("width", "0px") .transition() .delay(function(d, i) { return i * 100; }) .duration(500) .attr("width", function(d, i) { return xScale(d); });
// ボタンクリック時の処理 d3.select("#update-btn").on("click", function() { var dataArray = new Array(dataSet.length); for (var i = 0; i < dataArray.length; i++) { dataArray[i] = Math.floor(Math.random() * d3.max(dataSet)); } // rect要素のwidthを再設定 svg.selectAll("rect") .data(dataArray) .attr("width", "0px") .transition() .delay(function(d, i) { return i * 100; }) .duration(500) .attr("width", function(d, i) { return xScale(d); }); });
・transition()
transition()メソッド以後に、メソッドチェーンで指定された属性値に時間をかけて変化させる処理を行います。
属性値の初期値と最終値を設定してやるだけで、その間のアニメーションはD3.jsが勝手に補完してくれます。
・delay()
delay()メソッドは、アニメーションに待ち時間を持たせることが可能です。
delayメソッドは関数をパラメータとして渡してやることで、データセットのデータと順番を取得してくれます。
.delay(function(d, i) { return i * 100; })
上記の例では、各データごと、アニメーション開始にディレイを与えています。
・duration()
duration()メソッドは、アニメーション開始から終了までの時間を指定することが出来ます。
パラメータにはミリ秒単位で時間を指定します。
こちらの動画をクリックすることで、データの更新とアニメーションが確認出来ると思います。
sample_graph_animation
最終的なコードはこちらになります。
表示した段階で目盛り、棒線が欠けてしまっていたので、余白を設定し位置を微調整しています。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>D3.js Animation</title> <style> svg { border: 1px solid black; } #barGraph rect { stroke : rgb(0, 0, 130); stroke-width : 2px; fill : rgb(0, 0, 230); } .axis text { font-family: sans-serif; font-size: 15px; } .axis path, .axis line { fill: none; stroke: black; } </style> <script charset="utf-8" type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script> </head> <body> <h1>Sample Graph</h1> <button id="update-btn">Update</button> <div id="barGraph"></div> <script src="index.js"></script> </body> </html>
var svg_w = 550; // 幅 var svg_h = 300; // 高さ var padding = 20; var dataSet = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]; var xScale = d3.scale.linear() .domain([0, d3.max(dataSet)]) .range([0, svg_w - padding * 2]); var xAxis = d3.svg.axis() .scale(xScale) .orient("bottom"); var svg = d3.select("#barGraph") // barGraph要素を監視 .append("svg") // svg領域を追加 .attr({ width: svg_w, // 幅を設定 height: svg_h, // 高さを設定 }); // 目盛りを表示する svg.append("g") .attr("class", "axis") .attr("transform", "translate(" + padding + "," + ((dataSet.length + 1) * 20 + 50) + ")") .call(xAxis) // データに基づいて描画する svg.selectAll("rect") .data(dataSet) .enter() .append("rect") .attr("x", padding) .attr("y", function(d, i) { return i * 25 + 15; }) .attr("height", "20px") .attr("width", "0px") .transition() .delay(function(d, i) { return i * 100; }) .duration(500) .attr("width", function(d, i) { return xScale(d); }); // ボタンクリック時の処理 d3.select("#update-btn").on("click", function() { var newDataSet = new Array(dataSet.length); for (var i = 0; i < newDataSet.length; i++) { newDataSet[i] = Math.floor(Math.random() * d3.max(dataSet)); } svg.selectAll("rect") .data(newDataSet) .attr("width", "0px") .transition() .delay(function(d, i) { return i * 100; }) .duration(500) .attr("width", function(d, i) { return xScale(d); }); });
いかがでしたでしょうか。
アニメーション機能を実装することで、見ていて楽しい&見やすいグラフを作り上げることが可能だと感じました。