はじめに
ブロック図を書いてみてd3.jsはSVGの作図ツールとしても良いかもと思った - Qiitaでは、複数行テキストを矩形内で垂直・水平方向に中央揃えするように実装しました。今回は、これを発展させて、垂直方向の上寄せ・中央揃え・下寄せと水平方向の左寄せ・中央揃え・右寄せを選べるように機能拡張して、再利用可能なd3.jsプラグインとして実装してみました。
d3.jsのプラグインの作り方
d3.jsの拡張方法
d3.js - How to make a d3 plugin? - Stack Overflowにプラグインの作り方についてのQ&Aがあります。d3.selection のプロトタイプを拡張する例も紹介されていますが、プロトタイプ汚染を避けて 関数として実装 して selections.callを呼ぶのがスタンダードな方法とのことです。
作り方の例
以下の記事を参考にしました。
プラグインの構造
Simple example of reusable d3 plugin.の記事のプラグインの構造は以下の様な感じです。
d3.textBlock = function() {
// プロパティ定義とデフォルト値設定
var label = "";
function my(selection) {
selection.each(function(d, i) {
// ここにプラグイン固有の処理を実装
});
}
// labelプロパティのゲッター/セッター
my.label = function(value) {
if (!arguments.length) return value;
label = value;
return my;
};
return my;
}
d3.textBlock
はselections.callに渡す関数として定義するのではなく、そういう関数を返す関数(高階関数)として定義しています。
my関数
はクロージャになっていて、labelをプロパティとして定義しデフォルト値とゲッター/セッターを用意しています。
使い方の例
d3.textBlock()
でselections.callに渡す関数を作って、labelプロパティをデフォルト値から変えたい場合は以下のようにセッターを呼び出して変更します。
var tb = d3.textBlock().label(function(d) {return d.label;});
var item = svg.selectAll("rect")
.data(items)
.enter()
.append("svg:g")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.call(tb);
今回作成したプラグイン
- ソース: hnakamur/d3-multiline-text-plugin
- デモ: https://hnakamur.github.io/d3-multiline-text-plugin/
- デモソース: https://github.com/hnakamur/d3-multiline-text-plugin/blob/gh-pages/index.html
下記のスクリーンショットの上側の図(Common example)に対応するJSのコードは以下の通りです。
function createCommonExample() {
var frameWidth = 680;
var frameHeight = 84;
var svg = d3.select("#commonExample").append("svg")
.attr({
width: frameWidth,
height: frameHeight
});
var rects = [
{x: 20, y: 2, w: 200, h: 80, text: 'This is a common\nexample.'},
{x: 240, y: 2, w: 200, h: 80, text: 'The same alignments\nin these text blocks'},
{x: 460, y: 2, w: 200, h: 80, text: 'verticalAlign: center\nhorizontaiAlign: left'}
];
var g = svg.selectAll('g.block')
.data(rects).enter().append('g')
.attr({
class: 'block',
transform: function(d) {
return "translate(" + d.x + "," + d.y + ")";
},
});
g.append('rect')
.attr({
'width': function(d) { return d.w; },
'height': function(d) { return d.h; },
'fill': 'none',
'stroke': '#00C9F9',
'stroke-width': 1
});
g.append('text').call(d3.multilineText().verticalAlign('center').horizontalAlign('left'));
}
プラグインのソースでは以下の7つのプロパティを定義していて、上記の例ではそのうちverticalAlignとhorizontalAlignの2つを設定して残り5つはデフォルト値を使っています。
var lineHeight = 1.4;
var horizontalAlign = 'center'; // 'left', 'center', or 'right'
var verticalAlign = 'center'; // 'top', 'center', or 'bottom'
var paddingTop = 10;
var paddingBottom = 10;
var paddingLeft = 10;
var paddingRight = 10;
スクリーンショット
感想
ブロック図を書いてみてd3.jsはSVGの作図ツールとしても良いかもと思った - Qiitaのコードと比べると利用側のコードはずいぶんとスッキリしました。
矩形を描く部分などもプラグイン化していけば、利用する側はデータ定義とプラグイン呼び出しだけで良くなっていくので、さらに快適になりそうです。