D3.js v4/v5 棒グラフを並び替える方法
棒グラフをアニメーションとともに並び替える方法を紹介します。
サンプルデモ – 横軸のメモリをクリックorタッチしてください
サンプルプログラム
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 |
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>D3 Bar Chart Sort</title> <script src="https://d3js.org/d3.v5.min.js"></script> </head> <body> <script> var dataset = [ { "name": "A", "value": 5 }, { "name": "B", "value": 6 }, { "name": "C", "value": 8 }, { "name": "D", "value": 1 }, { "name": "E", "value": 2 }, { "name": "F", "value": 6 }, { "name": "G", "value": 8 }, { "name": "H", "value": 6 }, { "name": "I", "value": 10 }, { "name": "J", "value": 9 } ] var width = 600; var height = 400; var padding = 30; var svg = d3.select("body").append("svg").attr("width", width).attr("height", height); // 1.並べ替え用スケール設定 var xScale1 = d3.scaleBand() .rangeRound([padding, width - padding]) .padding(0.1) .domain(dataset.map(function(d) { return d.name; })); dataset.sort(function(a, b) { if(a.value < b.value) return 1; if(a.value > b.value) return -1; return 0; }); var xScale2 = d3.scaleBand() .rangeRound([padding, width - padding]) .padding(0.1) .domain(dataset.map(function(d) { return d.name; })); dataset.sort(function(a, b) { if(a.name < b.name) return -1; if(a.name > b.name) return 1; return 0; }); var yScale = d3.scaleLinear() .domain([0, d3.max(dataset, function(d) { return d.value; })]) .range([height - padding, padding]); var axisx = d3.axisBottom(xScale1); var axisy = d3.axisLeft(yScale); // 2.クリック時のイベント設定 var flg = 0; svg.append("g") .attr("transform", "translate(" + 0 + "," + (height - padding) + ")") .call(axisx) .attr("class", "xScale") .attr("cursor", "pointer") .selectAll(".tick") .data(dataset) .on("click", function() { if(flg == 0) { d3.selectAll(".bar") .transition() .duration(500) .attr("x", function(d) { return xScale2(d.name); }); d3.selectAll(".xScale .tick") .transition() .duration(500) .attr("transform", function(d) { return "translate(" + ((xScale2.bandwidth() + xScale2.padding()) / 2 + xScale2(d.name)) + ",0)" }); flg = 1; } else { d3.selectAll(".bar") .transition() .duration(500) .attr("x", function(d) { return xScale1(d.name); }); d3.selectAll(".xScale .tick") .transition() .duration(500) .attr("transform", function(d) { return "translate(" + ((xScale1.bandwidth() + xScale1.padding()) / 2 + xScale1(d.name)) + ",0)" // }); flg = 0; } }); svg.append("g") .attr("transform", "translate(" + padding + "," + 0 + ")") .call(axisy); svg.append("g") .selectAll("rect") .data(dataset) .enter() .append("rect") .attr("class", "bar") .attr("x", function(d) { return xScale1(d.name); }) .attr("y", function(d) { return yScale(d.value); }) .attr("width", xScale1.bandwidth()) .attr("height", function(d) { return height - padding - yScale(d.value); }) .attr("fill", "steelblue"); </script> </body> </html> |
解説
基本的な棒グラフの作り方はこちらを参照してください。
並び替えイベントに関連する部分を解説します。
1. 並べ替え用スケール設定
まず、通常の軸用のスケールを設定します。
1 2 3 4 |
var xScale1 = d3.scaleBand() .rangeRound([padding, width - padding]) .padding(0.1) .domain(dataset.map(function(d) { return d.name; })); |
.rangeRoundで軸の画面上の表示範囲を、domainで軸ラベル用の配列を設定します。dataset.map()でdatasetからnameを配列として取り出して設定します。
次に、データセットを並び替えます。
1 2 3 4 5 |
dataset.sort(function(a, b) { if(a.value < b.value) return 1; if(a.value > b.value) return -1; return 0; }); |
.sort()はjavascriptオブジェクトの標準メソッドで、引数に指定した関数の返り値が0より大きければb,aの順に、0より小さければa,bの順に、0の場合は並び替えないものです。今回は大きい順に並び替えたいので上の設定にしています。ちなみに対象が数値の場合は、
1 2 3 |
dataset.sort(function(a, b) { return b.value- a.value; }); |
としてもよいです。
次に、並び替えた後のdatasetに対して軸用のスケールを設定します。
1 2 3 4 |
var xScale2 = d3.scaleBand() .rangeRound([padding, width - padding]) .padding(0.1) .domain(dataset.map(function(d) { return d.name; })); |
イベントによって、xScale1とXScale2を切り替えます。
最初の表示用に軸を最初の配列の並びどおり、名前順に並び替えておきます。
1 2 3 4 5 |
dataset.sort(function(a, b) { if(a.name < b.name) return -1; if(a.name > b.name) return 1; return 0; }); |
2. クリック時のイベント設定
切り替え用にフラグを設定します。
1 |
var flg = 0; |
今回は横軸をクリック(タップ)した際に並び変わるようにイベントを設定します。
まず、軸を設定します。
1 2 3 |
svg.append("g") .attr("transform", "translate(" + 0 + "," + (height - padding) + ")") .call(axisx) |
並び変えの際にd3.selectAllで軸要素を選択するため、”class”属性を設定しておきます。また、クリックできることが分かるようにマウスカーソルを合わせたときにポインターが変わるようにします。
1 2 |
.attr("class", "xScale") .attr("cursor", "pointer") |
.call(axisx)で軸のSVG要素を一括で設定していますが、一括設定した軸のラベル要素は”tick”がクラスとして設定されています。バーとともに軸のラベルも移動させるためにデータを割り当てておきます。
1 2 |
.selectAll(".tick") .data(dataset) |
onメソッドを使ってクリック時のイベント関数を登録します。
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 |
.on("click", function() { if(flg == 0) { d3.selectAll(".bar") .transition() .duration(500) .attr("x", function(d) { return xScale2(d.name); }); d3.selectAll(".xScale .tick") .transition() .duration(500) .attr("transform", function(d) { return "translate(" + ((xScale2.bandwidth() + xScale2.padding()) / 2 + xScale2(d.name)) + ",0)" }); flg = 1; } else { d3.selectAll(".bar") .transition() .duration(500) .attr("x", function(d) { return xScale1(d.name); }); d3.selectAll(".xScale .tick") .transition() .duration(500) .attr("transform", function(d) { return "translate(" + ((xScale1.bandwidth() + xScale1.padding()) / 2 + xScale1(d.name)) + ",0)" // }); flg = 0; } }); |
flgの値によって、xScale1とxScale2を切り替えます。棒グラフのバーは”x”属性を、軸ラベルは”transform”属性を使って横方向の位置を設定します。.transition().duration(500)は500ミリ秒かけて設定する属性に切り替えるアニメーションを設定するものです。
まとめ
transitionがうまく使えるとインタラクティブ性が向上するかと思います。また、棒グラフの基本的な使い方はこちらを参照ください。