D3.js Grid(グリッド)の描画

Gridを描画する

grid.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <script src="https://d3js.org/d3.v7.js"></script>
  <script src="./js/grid.js"></script>
</head>
<body>
<div id="grid"></div>
<span id="data" style="display:none;">
    [
      [
        {"x":1,"y":1,"width":100,"height":100,"selected":false},
        {"x":101,"y":1,"width":250,"height":50,"selected":false}
      ],
      [
        {"x":101,"y":51,"width":100,"height":50,"selected":false},
        {"x":201,"y":51,"width":150,"height":50,"selected":false}
      ],
      [
        {"x":1,"y":101,"width":50,"height":300,"selected":false},
        {"x":51,"y":101,"width":50,"height":150,"selected":false},
        {"x":101,"y":101,"width":100,"height":150,"selected":false},
        {"x":201,"y":101,"width":150,"height":150,"selected":false}
      ],
      [
        {"x":51,"y":251,"width":50,"height":150,"selected":false},
        {"x":101,"y":251,"width":100,"height":150,"selected":false},
        {"x":201,"y":251,"width":150,"height":150,"selected":false}
      ]
    ]
    </span>
</body>
</html>

./js/grid.js

document.addEventListener("DOMContentLoaded", function () {
  // HTMLのJSON文字列をデシリアライズする。
  const gridData = JSON.parse(document.querySelector("#data").textContent);

  const grid = d3.select("#grid")
                 .append("svg")
                 .attr("width", "510px")
                 .attr("height", "510px");

  const row = grid.selectAll()
                  .data(gridData)
                  .enter()
                  .append("g")
                  .attr("class", "row");

  const noSelectedColor = "#ffffff";
  const selectedColor = "#2C93E8";
  const column = row.selectAll()
                    .data(d => {
                      return d;
                    })
                    .enter()
                    .append("rect")
                    .attr("class", "square")
                    .attr("x", function (d) {
                      return d.x;
                    })
                    .attr("y", function (d) {
                      return d.y;
                    })
                    .attr("width", function (d) {
                      return d.width;
                    })
                    .attr("height", function (d) {
                      return d.height;
                    })
                    .style("fill", noSelectedColor)
                    .style("stroke", "#222")
                    .on('click', function (d) {
                      d.selected = !d.selected;
                      let cell = d3.select(this);
                      if (d.selected) {
                        cell.style("fill", selectedColor);
                      } else {
                        cell.style("fill", noSelectedColor);
                      }
                    });
});

コード説明

上から順番に説明する。

svgタグ

const grid = d3.select("#grid")
               .append("svg")
               .attr("width", "510px")
               .attr("height", "510px");

実行後は次のように変更される。

<div id="grid">
  <svg width="510px" height="510px"></svg>
</div>

selectメソッドのセレクターは、JavaScriptやCSSなどと同じである。

  • 取得したd3.selectionオブジェクトに、appendメソッドでsvgタグを挿入する。
  • appendメソッドによってタグが実際に生成される。
  • appendメソッドの戻り値はd3.selectionオブジェクトである。
  • attrメソッドで属性を追加する。

dataメソッドにオブジェクトを割り当てる

const row = grid.selectAll()
                .data(gridData)
                .enter()
                .append("g")
                .attr("class", "row");

実行後は次のように変更される。

<div id="grid">
  <svg width="510px" height="510px">
    <g class="row"></g>
    <g class="row"></g>
    <g class="row"></g>
    <g class="row"></g>
  </svg>
</div>
  • dataメソッドでグリッドのオブジェクトを割り当てる。
  • この例ではJSON文字列からオブジェクトを生成する。
  • enterメソッドで、dataメソッドに割り当てられたオブジェクトがd3.selectionオブジェクトに設定される。
  • append("g")メソッドで、オブジェクトの各配列要素にg要素をバインドしてタグを生成する。

rectの生成

const noSelectedColor = "#ffffff";
const selectedColor = "#2C93E8";
const gridStrokeColor = "#222222";
  
const column = row.selectAll()
                  .data(d => d)
                  .enter()
                  .append("rect")
                  .attr("class", "square")
                  .attr("x", d => d.x)
                  .attr("y", d => d.y)
                  .attr("width", d => d.width)
                  .attr("height", d => d.height)
                  .style("fill", noSelectedColor)
                  .style("stroke", gridStrokeColor)
                  .on('click', function (d) {
                    d.selected = !d.selected;
                    let cell = d3.select(this);
                    if (d.selected) {
                      cell.style("fill", selectedColor);
                    } else {
                      cell.style("fill", noSelectedColor);
                    }
                  });

実行後は次のように変更される。

<div id="grid">
  <svg width="510px" height="510px">
    <g class="row">
      <rect class="square" x="1" y="1" width="100" height="100" style="fill: rgb(255, 255, 255); stroke: rgb(34, 34, 34);">
      </rect>
      <rect class="square" x="101" y="1" width="250" height="50" style="fill: rgb(255, 255, 255); stroke: rgb(34, 34, 34);">
      </rect>
    </g>
    <g class="row">
      <rect class="square" x="101" y="51" width="100" height="50" style="fill: rgb(255, 255, 255); stroke: rgb(34, 34, 34);">
      </rect>
      <rect class="square" x="201" y="51" width="150" height="50" style="fill: rgb(255, 255, 255); stroke: rgb(34, 34, 34);">
      </rect>
    </g>
    <g class="row">
      <rect class="square" x="1" y="101" width="50" height="300" style="fill: rgb(255, 255, 255); stroke: rgb(34, 34, 34);">
      </rect>
      <rect class="square" x="51" y="101" width="50" height="150" style="fill: rgb(255, 255, 255); stroke: rgb(34, 34, 34);">
      </rect>
      <rect class="square" x="101" y="101" width="100" height="150" style="fill: rgb(255, 255, 255); stroke: rgb(34, 34, 34);">
      </rect>
      <rect class="square" x="201" y="101" width="150" height="150" style="fill: rgb(255, 255, 255); stroke: rgb(34, 34, 34);">
      </rect>
    </g>
    <g class="row">
      <rect class="square" x="51" y="251" width="50" height="150" style="fill: rgb(255, 255, 255); stroke: rgb(34, 34, 34);">
      </rect>
      <rect class="square" x="101" y="251" width="100" height="150" style="fill: rgb(255, 255, 255); stroke: rgb(34, 34, 34);">
      </rect>
      <rect class="square" x="201" y="251" width="150" height="150" style="fill: rgb(255, 255, 255); stroke: rgb(34, 34, 34);">
      </rect>
    </g>
  </svg>
</div>
  • ここでは、先にg要素へバインドした配列の行オブジェクトの要素からrectを生成する。
  • xywidthheightfillstrokeはSVGの属性である。
  • jQueryと同じように、onメソッドでイベントを付与できる。
  • ここではクリックすると背景色を切り替えるイベントを付与している。