d3交互-平移缩放(d3-zoom)

177 阅读2分钟

引言

d3-zoom使用直接操作:单击并拖动以平移(平移)、旋转滚轮以缩放(缩放)或触摸捏合。

更加详细的示例可以查看示例·平移和缩放功能。本文只记录最基础的使用。

本文中使用到的d3-zoom方法有:

  • transform 指定缩放变换
  • scaleExtent 设置缩放范围
  • translateBy 初始平移位置
  • scaleBy 设置初始缩放比例

这几个方法就可以完成一些基本的需求,比如缩放重置、某个元素居中等功能。

d3-zoom基础使用

定义缩放行为

const zoom = d3.zoom()
  .scaleExtent([0.3, 10]) // 缩放范围
  .on("zoom", (event) => {
    g.attr("transform", event.transform);
  });
zoom.translateBy(svg, width / 2, height / 2) // 初始平移位置
// 将缩放行为绑定到 SVG 容器
svg.call(zoom);

初始化缩放状态

function initializeZoom() {
      const initialScale = 1; // 初始缩放比例
      const initialTranslate = [width / 2, height / 2]; // 初始平移位置

      // 设置初始缩放状态
      svg.transition().duration(1000) // 可选:添加动画效果
        .call(zoom.transform, d3.zoomIdentity.translate(initialTranslate[0], initialTranslate[1]).scale(initialScale));
    }

完整示例

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>Radial Tree with D3.js</title>
  <script src="https://d3js.org/d3.v7.min.js"></script>
  <style>
    .node {
      cursor: pointer;
    }

    .node circle {
      fill: #999;
    }

    .node text {
      font: 10px sans-serif;
    }

    .link {
      fill: none;
      stroke: #555;
      stroke-opacity: 0.4;
      stroke-width: 1.5px;
    }
  </style>
</head>

<body>
  <svg width="600" height="600"></svg>
  <button id="reset">重置</button>
  <script>
    const data = {
      name: "Root",
      children: [
        {
          name: "Child 1",
          children: [
            { name: "Grandchild 1" },
            { name: "Grandchild 2" },
          ],
        },
        {
          name: "Child 2",
          children: [
            { name: "Grandchild 3" },
            { name: "Grandchild 4" },
          ],
        },
      ],
    };

    const svg = d3.select("svg")
    const width = +svg.attr("width");
    const height = +svg.attr("height");
    const radius = Math.min(width, height) / 2;

    // 创建树布局
    const tree = d3.tree().size([2 * Math.PI, radius - 100]);

    // 使用 d3.hierarchy 将数据转换为树结构
    const root = d3.hierarchy(data);

    // 布局树
    tree(root);
    // 创建一个组元素,用于平移和缩放
    const g = svg.append("g")


    // 绘制连接线
    const linkRadial = d3
      .linkRadial()
      .angle((d) => d.x)
      .radius((d) => d.y)

    const link = g.selectAll(".link")
      .data(root.descendants().slice(1)) //过滤掉顶层节点
      .enter().append("path")
      .attr("class", "link")
      .attr("d", d => {
        return linkRadial({ source: d, target: d.parent })
      });

    // 绘制节点
    const node = g.selectAll(".node")
      .data(root.descendants())
      .enter().append("g")
      .attr("class", d => "node" + (d.children ? " node--internal" : " node--leaf"))
      .attr("transform", d => `rotate(${(d.x - Math.PI / 2) / Math.PI * 180}) translate(${d.y},0)`);

    // 添加节点圆圈
    node.append("circle")
      .attr("r", 4.5);

    // 添加节点文本
    node.append("text")
      .attr("dy", ".31em")
      .attr("x", d => d.x < Math.PI ? 8 : -8)
      .attr("text-anchor", d => d.x < Math.PI ? "start" : "end")
      .attr("transform", d => d.x >= Math.PI ? "rotate(180)" : "")
      .text(d => d.data.name);

    // 定义缩放行为
    const zoom = d3.zoom()
      .scaleExtent([0.3, 10]) // 缩放范围
      .on("zoom", (event) => {
        g.attr("transform", event.transform);
      });
    zoom.translateBy(svg, width / 2, height / 2) // 初始平移位置
    // 将缩放行为绑定到 SVG 容器
    svg.call(zoom);

    // 初始化缩放状态
    function initializeZoom() {
      const initialScale = 1; // 初始缩放比例
      const initialTranslate = [width / 2, height / 2]; // 初始平移位置

      // 设置初始缩放状态
      svg.transition().duration(1000) // 可选:添加动画效果
        .call(zoom.transform, d3.zoomIdentity.translate(initialTranslate[0], initialTranslate[1]).scale(initialScale));
    }
    document.getElementById("reset").addEventListener("click", () => {
      initializeZoom()
    });
  </script>
</body>

</html>