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 문자열을 deserialize 한다.
  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 메소드의 셀렉터는 js, 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 를 생성한다.
    • x, y, width, height, fill, stroke는 SVG의 속성이다.
  • jQuery와 같이 on 메소드로 이벤트를 부여할 수 있다.
    • 여기에서는 클릭하면 배경색을 전환하는 이벤트를 부여하고 있다.



최종 수정 : 2024-01-18