株式会社スマレジの開発部でスマレジのサーバサイドを作っています

チケットの見える化にチャレンジしてみる(3-3: 準備編 D3.jsの導入(3:SVGの導入))

こんにちは!株式会社スマレジ 、開発部のmasaです。

2022年になって、弊社でも新入社員さんが続々と入ってきています! スマレジ はまだまだ作りたいもの・やりたいことがたくさんあるので、もちろんそれに応じて人も積極採用中です! ちょっとでも興味を持たれた方は、下記採用ページを是非見てみてください。

corp.smaregi.jp

さて、今回はD3.jsの真骨頂、SVGを扱ったグラフの描画を行います。

グラフにするならやっぱりベクター画像(SVG)

htmlの要素でもグラフ描画が可能なのは前回・前々回を通じて見てきました。 しかし、html要素では拡大縮小などの処理が煩雑だったり、処理に失敗すればぼやけたりします。

そういったダサい描画を避ける手段として、SVG要素を使用する方法があります。 前回から扱っている棒グラフに下から上にニョキっと生えるアニメーションをつけた例を作成したので、 それをベースに説明していきます。

実装例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>D3.jsの練習</title>
</head>
<body>
<div id="chart-area"></div>

<script type="text/javascript" src="js/d3.min.js"></script>
<script type="text/javascript">

    /**
     * divで棒グラフを作る。
     * @param data bindする配列データ
     */
    function drawBarChart(data) {
        const WIDTH_MAX = 300;
        const HEIGHT_MAX = 300;
        const svg = d3.create("svg")
            .style("width", WIDTH_MAX)
            .style("height", HEIGHT_MAX)
            .attr("id", "draw-area");
        const rects = svg.selectAll("rect")
            .data(data)
            .enter()
            .append("rect");
        rects
            .attr("width", 10)
            .attr("height", (d) => d * 10)
            .attr("x", (d, i) => i * 10)
            .attr("y", (d) => 300 - d * 10)
            .attr("fill", "steelblue")
            .attr("class", "bar")
        svg.append("rect")
            .attr("width", WIDTH_MAX)
            .attr("height", HEIGHT_MAX)
            .attr("fill", "white")
            .attr("id", "mask")

        document.getElementById("chart-area").appendChild(svg.node());
    }

    /**
     * 棒グラフをアニメーションさせる。
     */
    function animateBarChart() {
        const transitionStyle = d3.transition()
            .duration(1000)
            .ease(d3.easePoly)
        d3.selectAll("#mask")
            .transition(transitionStyle)
            .attr("y", -300)
    }

    let bindArray = [10,3,6,8,22,15,1];
    drawBarChart(bindArray);
    animateBarChart();
</script>
</body>
</html>

どんな動きになるのかは、コピペしてみてみてください。(d3のライブラリがセットで必要なので注意)

説明

前回と大きく変わったのはdrawBarChartなので、そこを中心に説明していきます。

SVG領域の追加

        const WIDTH_MAX = 300;
        const HEIGHT_MAX = 300;
        const svg = d3.create("svg")
            .style("width", WIDTH_MAX)
            .style("height", HEIGHT_MAX)
            .attr("id", "draw-area");

SVGは画像扱いなので、幅と高さを指定して、html内に埋め込んであげる必要があります。 この辺りは、jQueryなどとあまり感覚に差はないと思います。(使う命令が違っているくらいのもの)

矩形の定義とデータバインド

        const rects = svg.selectAll("rect")
            .data(data)
            .enter()
            .append("rect");
        rects
            .attr("width", 10)
            .attr("height", (d) => d * 10)
            .attr("x", (d, i) => i * 10)
            .attr("y", (d) => 300 - d * 10)
            .attr("fill", "steelblue")
            .attr("class", "bar")

次に棒グラフの「棒」の部分を定義してあげます。 棒の数と高さは、与えられるデータによって変更したいものです。 そういった変更したいデータと矩形を紐づけるときに使うのが、data()メソッドです。 では、データを紐付けたら実際に棒グラフをsvg領域に「追加」しなければいけません。 この「追加」に相当するのがenter()enter()以降はどんな要素(今回ではrect要素)にどんなスタイルで(今回は幅10固定で高さは入力データの各要素を10倍したもので、色はsteelblueでsvg領域に下揃え。)追加するのかを記載しています。

作成した棒グラフを隠すためにマスク用矩形を用意する。

        svg.append("rect")
            .attr("width", WIDTH_MAX)
            .attr("height", HEIGHT_MAX)
            .attr("fill", "white")
            .attr("id", "mask")

svg領域全体を覆うように白の矩形を描画します。後に定義したものはレイヤが上がるので、 初期状態では棒グラフが見えないようになります。

マスク用矩形を上に動かして、アニメーションで出現させる

    /**
     * 棒グラフをアニメーションさせる。
     */
    function animateBarChart() {
        const transitionStyle = d3.transition()
            .duration(1000)
            .ease(d3.easePoly)
        d3.selectAll("#mask")
            .transition(transitionStyle)
            .attr("y", -300)
    }

SVG領域は一番左上の角が(0,0)になるように作成されます。 なので、yに負の値を与えてあげて、transitionさせることで矩形が上に上がってきます。

マスク用の矩形を棒グラフの棒の数だけ作成し、時間差をつけて同じように上方向にtranditionすれば左から順番に 棒がニョキニョキ生えてくるようにも見えますね。

次回からは、いよいよredmineとの連携を考えていきます。