d3は高機能な描画機能が売りの一つだと思いますが、一方で柔軟な表現が出来るがために、シンプルなチャートを描画するだけでも多くのコード量が必要になりますし、コードそのものも難解なものになりがちです。そこで記述を完結にするためにd3をベースにしたRickshaw、C3.jsといったチャート用のライブラリが増えてきています。
Rickshaw
http://code.shutterstock.com/rickshaw/
Rickshawでのチャート |
http://c3js.org/
上記は下記のようなJSの記述でチャートを生成しますが、
var chart = c3.generate({
data: {
rows: [
['data1', 'data2', 'data3'],
[90, 120, 300],
[40, 160, 240],
[50, 200, 290],
[120, 160, 230],
[80, 130, 300],
[90, 220, 320],
]
}
});
http://www.fullscale.co/dangle/
や
Radian
http://openbrainsrc.github.io/Radian/index.html
はAngular JSのDirectiveを用いて、htmlを記述することで複雑なチャートも簡潔に記述ができるようになっています。Radianでの記述は以下のようになります。
<plot height=200 aspect=2 stroke-width=2
x="[[seq(0,4*PI,101)]]"
axis-x-label="Time"
axis-y-label="sin(x) / cos(x)">
<lines y="[[sin(x)]]" stroke="red"></lines>
<lines y="[[cos(x)]]" stroke="blue"></lines>
</plot>
Radianのチャート |
こちらです。
https://github.com/lithiumtech/angular_and_d3
Step1ではAngularを使わずにゲージの表示を行っていますが、ステップを進む毎にAngularのintegrationが行われています。またゲージを表示するClassは既に作られており、それは改変せずにAngularからどのように呼び出すか?というチュートリアルになっています。(上記の細かいスッテップは省きます。)
1-1: Angularを使わないhtml
<body onload="initialize()">
<div>
<span id="memoryGaugeContainer"></span>
<span id="cpuGaugeContainer"></span>
<span id="networkGaugeContainer"></span>
</div>
</body>
<body ng-controller="MyController">
<div>
<gauge min="-50" max="50" value="values.p" label="Petrol"></gauge>
<gauge min="-50" max="50" value="values.o" label="Oil"></gauge>
<gauge min="-50" max="50" value="values.c" label="Coolant"></gauge>
</div>
<gauge>というタグを定義し、min、maxなどの独自のattributeを設定する。またbodyタグに ng-controller="MyController"属性を付与し、body以下をMyControllerのscopeとする。
2-1: Angularを使わないJS
var gauges = [];
function createGauge(name, label, min, max) {
var config = {
size: 250,
label: label,
min: undefined != min ? min : 0,
max: undefined != max ? max : 100,
minorTicks: 5
}
var range = config.max - config.min;
config.yellowZones = [{
from: config.min + range * 0.75,
to: config.min + range * 0.9
}];
config.redZones = [{
from: config.min + range * 0.9,
to: config.max
}];
gauges[name] = new Gauge(d3.select("#" + name + "GaugeContainer")[0][0], config);
gauges[name].render();
}
function createGauges() {
createGauge("memory", "Memory");
createGauge("cpu", "CPU");
createGauge("network", "Network");
}
function updateGauges() {
for (var key in gauges) {
var value = getRandomValue(gauges[key])
gauges[key].redraw(value);
}
}
function getRandomValue(gauge) {
var overflow = 0; //10;
return gauge.config.min - overflow + (gauge.config.max - gauge.config.min + overflow * 2) * Math.random();
}
function initialize() {
createGauges();
setInterval(updateGauges, 5000);
}
angular.module('components', []).directive('gauge', function() {
//componentsモジュールにgaugeディレクティブを追加します
return {
restrict: 'E', //Elementとしてdirectiveを使用する
replace: true, //Eを使用するときは、HTML として不適当な要素名が記述される可能せがあるのでtrueが望ましい、trueにするとgauseタグが置き換わる
scope: {
label: "@", // interpolate(値、string)
min: "=", // data bind
max: "=", // data bind
value: "=" // data bind
},
link: function(scope, element, attrs, ngModelCtrl) {
//linkでは、scopeにあるモデルの変更を検知、またイベントに応じた処理を記述して、DOM や controller とのやり取りを行う
//以下のコードは基本的にAngularを使わないJSと同じ
var config = {
size: 120,
label: attrs.label,
min: undefined != scope.min ? scope.min : 0,
max: undefined != scope.max ? scope.max : 100,
minorTicks: 5
};
var range = config.max - config.min;
config.yellowZones = [{
from: config.min + range * 0.75,
to: config.min + range * 0.9
}];
config.redZones = [{
from: config.min + range * 0.9,
to: config.max
}];
scope.gauge = new Gauge(element[0], config);
scope.gauge.render();
scope.gauge.redraw(scope.value);
//scopeに変更があったときに実行される処理
scope.$watch('value', function() {
if (scope.gauge)
scope.gauge.redraw(scope.value);
});
}
}
});
3: Jsdo.itのサンプル
このようにDirectiveを使うことで、html構文としてD3で作られる図を宣言することが出来、ロジックと図の宣言を明快に分けることができます。
コードの可読性も高まり、また再利用性も高まるのではないでしょうか?
参考:
AngularJS Directive なんてこわくない(その1)
0 件のコメント:
コメントを投稿