d3数据可视化实践-径向树

效果图展示:

实现思路:

使用 D3.js 的 d3.tree() 和 d3.stratify() 来创建树布局,并将其转换为径向布局。

实现步骤

1、svg

html中准备一个svg元素,也可以通过js创建

arduino 复制代码
<svg width="600" height="600"></svg>

确定画布大小、径向图半径

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

2、创建树布局

scss 复制代码
 const tree = d3.tree().size([2 * Math.PI, radius - 100]); //角度2PI,半径radius - 100

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

3、创建一个组元素,控制径向图居中

ini 复制代码
const g = svg.append("g").attr("transform", `translate(${width / 2},${height / 2})`);

4、绘制连线

javascript 复制代码
//径向链接生成器类似于笛卡尔链接生成器,只是x和y访问器被角度和半径访问器取代。
//返回具有径向切线的新链接生成器
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 })
      });

5、绘制节点

javascript 复制代码
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)`);

6、添加节点圆圈

go 复制代码
node.append("circle")
      .attr("r", 4.5);

7、添加节点文本

javascript 复制代码
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);

8、完整代码

xml 复制代码
<!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>
  <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").attr("transform", `translate(${width / 2},${height / 2})`);
    // 绘制连接线
    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);
  </script>
</body>

</html>
相关推荐
时间海87125 分钟前
对象键清洗之前,先拦住重名
javascript·后端
挖掘狂人1 小时前
Java 26 深度解析:一次“性能兑现 + 契约收紧”的版本
javascript·后端·性能优化
霍营_托尼1 小时前
微信小程序点餐项目实战:飞入动画、样式错位、弹窗不消失,7 个前端坑的完整排障记录
javascript·微信小程序·动效
Hilaku2 小时前
为什么我劝应届生不要去创业公司写前端?
前端·javascript·程序员
晓得迷路了3 小时前
栗子前端技术周刊第 149 期 - Vite+ 1.0、Preact 11、Next.js 16.4...
前端·javascript·react.js
前端一小卒3 小时前
搜索 API 额度用完返回空数组,Agent 就开始一本正经地胡说
前端·javascript·node.js
w2sfot4 小时前
AI时代,JS加密还有效吗?
开发语言·前端·javascript
haluhalu.4 小时前
Agent = LLM + 上下文 + 工具(含观察 / 动作空间、ReAct 循环)
前端·javascript·人工智能·python·react.js·机器学习·个人开发
子兮曰9 小时前
把 649 种文件格式塞进一个网页里:一个「文件不出浏览器」的预览器
前端·javascript·后端
paopaokaka_luck12 小时前
基于springboot+vue的智慧家政管理系统(AI问答、 WebSocket实时沟通、ECharts运营分析、订单规则与服务追踪)
java·javascript·spring boot·数据分析·echarts·mybatis