はるなぴログ

WEBサイト作成技術研究

d3-graphvizを使ってブラウザ上にフローチャート図を表示しよう

d3-graphvizを使用してブラウザ上にフローチャート図を表示する方法について説明します。

D3.jsとGraphvizを使用して動的グラフをブラウザで表示してみましょう。

いちいち画像ファイルを出力しなくともDOT言語で記述したグラフをブラウザに表示することができ大変便利です!

f:id:hal7pi:20190120141843j:plain

d3-graphvizとは

d3-graphvizとはDOT言語で書かれたファイルをGraphvizを用いてブラウザ上に表示させるオープンソース・ソフトウェアです。

Githubにソースがあります。

github.com

D3.jsとはブラウザ上で動的にコンテンツを扱うJavaScriptライブラリです。

公式サイトはこちらをご参照ください。

d3js.org

d3-graphvizはGraphvizを使ってDOTファイルからSVG画像を作成し、それを動的にアニメーションとして表示させることが可能です。

もちろんアニメーション効果なしで静的にフローチャート図などをブラウザに表示させることもできます。

前回の記事ではChocolateyを使ってWindowsにGraphvizをインストールしました。 (こちらの記事をご参照ください)

www.halu7.com

インストールしたGraphvizを使ってPNG画像やJPEG画像、SVG画像を作ることが可能です。

ウェブブラウザにフローチャート図を表示する

今回はDOT言語からグラフを作成しJavaScriptを利用して直接ブラウザで表示します。

ソースファイルを直接編集することで表示するグラフを変えることが可能になり、今までのようにGraphvizのコマンドを打って画像ファイルを生成したり、HTMLを編集したりする必要がなくなります。

d3-Graphvizの使い方

レンタルサーバーや独自サーバを使っている方はnpmでサーバーサイドに実装する方法もありますが、CDNが提供されていますのでこれを利用するのが簡単です。

ただし表示されるまでに若干の時間がかかります。 Mathjaxで数式を表示させた時と同様です。

<script src="//d3js.org/d3.v4.min.js"></script>
<script src="https://unpkg.com/viz.js@1.8.0/viz.js" type="javascript/worker"></script>
<script src="https://unpkg.com/d3-graphviz@1.4.0/build/d3-graphviz.min.js"></script>

上のスクリプトをHTML内に記述します。

Mathjaxの時は表示する順番を考えフッターや記事本文最後に埋め込みしましたが、今回はうまく動きません。

このスクリプトの後にDOT言語でグラフを記述しますので、その前に書く必要があります。

はてなブログで使う方法

はてなブログではHTMLのhead部分に追加記述する内容を指定できますが、d3-graphvizを使うページが大部分を占めるサイトはそうそう無いと思います。

従い、各記事本文の冒頭かあるいはグラフを記述する部分の直前に書くのが良いと思います。

フローチャート図

先ほど描いたグラフの記述を見てみましょう。

<script src="//d3js.org/d3.v4.min.js"></script>
<script src="https://unpkg.com/viz.js@1.8.0/viz.js" type="javascript/worker"></script>
<script src="https://unpkg.com/d3-graphviz@1.4.0/build/d3-graphviz.min.js"></script>
<div id="graph1" style="text-align: center;"></div>
<script>

d3.select("#graph1").graphviz()
    .fade(false)
    .renderDot('digraph  {a -> b}');

</script>

divタグ内のidでgraph1というグラフ名を指定しています。

これがDOT記法内の#graph1に対応しています。 これが一致していないとグラフは描画されません。

アニメーション効果付き

次はアニメーション効果つきのグラフを表示してみましょう。

これができるのがD3.jsのスゴイところです。

<div id="graph2" style="text-align: center;"></div>
<script>

var dotIndex = 0;
var graphviz = d3.select("#graph2").graphviz()
    .transition(function () {
        return d3.transition("main")
            .ease(d3.easeLinear)
            .delay(500)
            .duration(1500);
    })
    .logEvents(true)
    .on("initEnd", render);

function render() {
    var dotLines = dots[dotIndex];
    var dot = dotLines.join('');
    graphviz
        .renderDot(dot)
        .on("end", function () {
            dotIndex = (dotIndex + 1) % dots.length;
            render();
        });
}

var dots = [
    [
        'digraph  {',
        '    node [style="filled"]',
        '    a [fillcolor="#d62728"]',
        '    b [fillcolor="#1f77b4"]',
        '    a -> b',
        '}'
    ],
    [
        'digraph  {',
        '    node [style="filled"]',
        '    a [fillcolor="#d62728"]',
        '    c [fillcolor="#2ca02c"]',
        '    b [fillcolor="#1f77b4"]',
        '    a -> b',
        '    a -> c',
        '}'
    ],
    [
        'digraph  {',
        '    node [style="filled"]',
        '    a [fillcolor="#d62728"]',
        '    b [fillcolor="#1f77b4"]',
        '    c [fillcolor="#2ca02c"]',
        '    a -> b',
        '    a -> c',
        '}'
    ],
    [
        'digraph  {',
        '    node [style="filled"]',
        '    a [fillcolor="#d62728", shape="box"]',
        '    b [fillcolor="#1f77b4", shape="parallelogram"]',
        '    c [fillcolor="#2ca02c", shape="pentagon"]',
        '    a -> b',
        '    a -> c',
        '    b -> c',
        '}'
    ],
    [
        'digraph  {',
        '    node [style="filled"]',
        '    a [fillcolor="yellow", shape="star"]',
        '    b [fillcolor="yellow", shape="star"]',
        '    c [fillcolor="yellow", shape="star"]',
        '    a -> b',
        '    a -> c',
        '    b -> c',
        '}'
    ],
    [
        'digraph  {',
        '    node [style="filled"]',
        '    a [fillcolor="#d62728", shape="triangle"]',
        '    b [fillcolor="#1f77b4", shape="diamond"]',
        '    c [fillcolor="#2ca02c", shape="trapezium"]',
        '    a -> b',
        '    a -> c',
        '    b -> c',
        '}'
    ],
    [
        'digraph  {',
        '    node [style="filled"]',
        '    a [fillcolor="#d62728", shape="box"]',
        '    b [fillcolor="#1f77b4", shape="parallelogram"]',
        '    c [fillcolor="#2ca02c", shape="pentagon"]',
        '    a -> b',
        '    a -> c',
        '    b -> c',
        '}'
    ],
    [
        'digraph  {',
        '    node [style="filled"]',
        '    a [fillcolor="#d62728"]',
        '    b [fillcolor="#1f77b4"]',
        '    c [fillcolor="#2ca02c"]',
        '    a -> b',
        '    a -> c',
        '    c -> b',
        '}'
    ],
    [
        'digraph  {',
        '    node [style="filled"]',
        '    b [fillcolor="#1f77b4"]',
        '    c [fillcolor="#2ca02c"]',
        '    c -> b',
        '}'
    ],
    [
        'digraph  {',
        '    node [style="filled"]',
        '    b [fillcolor="#1f77b4"]',
        '}'
    ],
];

</script>

グラフを1ページ内に2つ以上書く場合はAPIの部分は一度書けば大丈夫です。

2つ目以降のグラフはidを変えるのを忘れないようにしてください。

このようなアニメーション効果がテキストによる指定だけで簡単にできてしまうのは本当に驚きですね!

d3-graphvizを使えばいちいち画像ファイルを出力してHTMLファイルに張り付ける手間なく、自動的にグラフをブラウザ表示させることが可能です。

d3-graphvizお勧めです!