D3.js zoom 사용법

D3의 마우스/터치 이벤트에 대응하는 zoom의 사용법을 소개한다.

예제 프로그램

그래프에서 드래그(Drag)/스와이프(Swipe), 마우스 휠(Mouse wheel)/핀치 인(Pinch in)해 본다. 아래의 재설정 버튼을 클릭하면 초기 위치로 돌아간다.

코드 확인

예제 코드

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>D3 v5 zoom</title>
  <script src="https://d3js.org/d3.v7.min.js"></script>
</head>

<body>

  <script>
    var width = 800;
    var height = 600; // 그래프 높이

    var svg = d3.select("body").append("svg").attr("width", width).attr("height", height);

    var dataset = [];
    for (var i = 0; i < 300; i++) {
      var r = 0.5 * d3.min([height, width]) * Math.random();
      var t = 2 * Math.PI * Math.random();
      dataset.push({
        "x": width / 2 + r * Math.cos(t),
        "y": height / 2 + r * Math.sin(t)
      });
    }

    // 축 설정
    var x = d3.scaleLinear().domain([0, width]).range([0, width]);
    var y = d3.scaleLinear().domain([0, height]).range([0, height]);

    var xAxis = d3.axisBottom(x)
      .ticks(width / height * 10) //
      .tickSize(height)
      .tickPadding(8 - height);

    var yAxis = d3.axisRight(y)
      .ticks(10)
      .tickSize(width)
      .tickPadding(8 - width);

    var gX = svg.append("g").call(xAxis);
    var gY = svg.append("g").call(yAxis);

    var view = svg.selectAll("circle")
      .data(dataset)
      .enter()
      .append("circle")
      .attr("class", "view")
      .attr("cx", function (d) { return d.x })
      .attr("cy", function (d) { return d.y })
      .attr("r", 5)
      .attr("fill", "steelblue")
      .attr("stroke", "black");

    d3.select("#resetButton")
      .on("click", resetted);

    var zoom = d3.zoom()
      .scaleExtent([1, 40])
      .translateExtent([
        [-100, -100],
        [width + 90, height + 100]
      ])
      .on("zoom", zoomed);

    svg.call(zoom);

    function zoomed(event) {
      view.attr("transform", event.transform);
      gX.call(xAxis.scale(event.transform.rescaleX(x)));
      gY.call(yAxis.scale(event.transform.rescaleY(y)));
    }

    function resetted() {
      svg.transition()
        .duration(750)
        .call(zoom.transform, d3.zoomIdentity);
    }
  </script>
  <div>
    <button id='resetButton'>reset</button>
  </div>

</body>

</html>

코드 설명

zoom 설정시에는 우선, d3.zoom()으로 zoom 이벤트의 프로토 타입을 호출하여 call 메소드로 zoom 이벤트를 등록하는 svg 요소를 설정한다. 여기서는 svg 요소를 설정하고 있지만 d3.select, d3.selectAll에서 선택한 요소에 등록할 수 있다.

var zoom = d3.zoom()
  .scaleExtent([1, 40])
  .translateExtent([
    [-100, -100],
    [width + 90, height + 100]
  ])
  .on("zoom", zoomed);

svg.call(zoom);

d3.zoom() 에는 다음과 같은 함수가 제공된다.

함수 설명
zoom.scaleExtent() 배율의 최소 최대값을 설정한다.
[k0, k1]과 같은 두 변수의 배열로 설정한다.
기본값은 [0, ∞]이다.
zoom.translateExtent() 이동 가능 범위를 설정한다.
[[x0, y0], [x1, y1]]과 같은 2차원 배열로 설정한다.
x0, y0은 왼쪽 상단의 좌표를, x1, x2는 오른쪽 하단의 좌표를 설정한다.
기본값은 [[-∞, -∞], [+∞, +∞]]이다.
zoom.on(typenames, listener) 이벤트를 등록한다. typenames에는 다음의 3종류를 설정할 수 있다.
  • start - zoom 시작시
  • zoom - zoom 시
  • end - zoom 종료시
listener 에는 이벤트시에 호출하는 함수를 등록한다. listenernull로 하면 등록하고 있던 이벤트가 해제된다.

on 함수는 이벤트 리스너(이벤트 시에 호출되거나 함수)를 등록한다.

  function zoomed() {
    view.attr("transform", d3.event.transform);
    gX.call(xAxis.scale(d3.event.transform.rescaleX(x)));
    gY.call(yAxis.scale(d3.event.transform.rescaleY(y)));
  }

zoom의 이벤트 리스너가 호출되면 d3.event는 현재 zoom 이벤트로 설정되고, 다음 필드가 설정된다.

메소드 설명
d3.event.target 관련 D3의 zoom behavior가 설정된다.
d3.event.type start, zoom, end 중 하나가 설정된다.
d3.event.transform 현재 줌 배율, 이동량이 설정된다.
d3.event.sourceEvent mousemove 및 touchmove와 같은 원래 입력 이벤트가 설정된다.

이벤트 리스너는 d3.event.transform을 사용하여 설정한다. d3.event.transformattr 함수를 사용하여 SVG 요소의 “transform” 속성에 할당하면 현재 배율로 변환된다. 스케일에는 transform로 설정되어 있는 다음의 함수를 사용해 변경할 수가 있다.

메소드 설명
transform.rescaleX(scale) 입력한 scale 의 domain 범위를 현재의 zoom값(x방향)에 맞추어 변경한다.
transform.rescaleY(scale) 입력한 scale 의 domain 범위를 현재 zoom 값(y 방향)에 맞게 변경한다.

마지막으로 리셋 버튼을 누를 때 원래 위치와 배율로 되돌리는 함수를 설정한다. d3.zoomIdentity에는 배율 0, 이동량 0의 transform 속성이 등록되어 있으며,

svg.call(zoom.transform, d3.zoomIdentity);

와 같이 call 메소드를 사용하여 호출하고 현재의 tranform 값으로 설정하면 초기 상태로 되돌릴 수 있다.

요약

마우스 조작뿐만 아니라 터치 이벤트, 핀치 인, 핀치 아웃에도 지원하고 있다.




최종 수정 : 2024-04-23