5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

複数行テキストを垂直・水平方向に揃えるd3.jsプラグインを作ってみた

Posted at

はじめに

ブロック図を書いてみて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.textBlockselections.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);

今回作成したプラグイン

下記のスクリーンショットの上側の図(Common example)に対応するJSのコードは以下の通りです。

https://github.com/hnakamur/d3-multiline-text-plugin/blob/6b6108a250b456a22dcc96f92b66281fc2c68a9e/index.html#L19-L54
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つはデフォルト値を使っています。

https://github.com/hnakamur/d3-multiline-text-plugin/blob/dba75a9061d33d234b4de5974ac1e9a1bbbb4349/multiline-text.js#L2-L8
  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-multiline-text-plugin-examples.png

感想

ブロック図を書いてみてd3.jsはSVGの作図ツールとしても良いかもと思った - Qiitaのコードと比べると利用側のコードはずいぶんとスッキリしました。

矩形を描く部分などもプラグイン化していけば、利用する側はデータ定義とプラグイン呼び出しだけで良くなっていくので、さらに快適になりそうです。

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?