【D3.js - v5.x】(6)绘制树状图 | 层级布局 | 附完整代码

简介: 【D3.js - v5.x】(6)绘制树状图 | 层级布局 | 附完整代码

树状图

在d3 中,绘制树状图,要用到层级布局这个概念:

d3.hierarchy(data[, children])

根据指定的层次结构数据构造一个根节点。指定的数据 data 必须为一个表示根节点的对象。比如:

{
  "name": "Eve",
  "children": [
    {
      "name": "Cain"
    },
    {
      "name": "Seth",
      "children": [
        {
          "name": "Enos"
        },
        {
          "name": "Noam"
        }
      ]
    },
    {
      "name": "Abel"
    },
    {
      "name": "Awan",
      "children": [
        {
          "name": "Enoch"
        }
      ]
    },
    {
      "name": "Azura"
    }
  ]
}

指定的 children 访问器会为每个数据进行调用,从根 data 开始,并且必须返回一个数组用以表示当前数据的子节点,返回 null 表示当前数据没有子节点。如果没有指定 children 则默认为:

function children(d) {
  return d.children;
}

返回的节点和每一个后代会被附加如下属性:

文档:https://www.d3js.org.cn/document/d3-hierarchy/#installing

其中,

  • node.descendants():返回后代节点数组,第一个节点为自身,然后依次为所有子节点的拓扑排序
  • node.links():返回当前 nodelinks 数组, 其中每个 link 是一个定义了 sourcetarget 属性的对象。每个 linksource 为父节点, target 为子节点。

同时,需要和tree生成器一起使用,来得到绘制树所需要的节点数据和边数据。

文档: https://www.d3js.org.cn/document/d3-hierarchy/#tree

其中,

  • d3.tree(),创建一个树状图生成器,使用默认的设置创建一个新的树布局
  • d3.tree().size([size]),定义树的大小。如果指定了 size 则设置当前系统树布局的尺寸为一个指定的二元数值类型数组,表示 [width, height] 并返回当前树布局。如果 size 没有指定则返回当前系统树布局的尺寸,默认为 [1, 1]。如果返回的布局尺寸为 null 时则表示实际的尺寸根据 node size 确定。
  • d3.tree.nodeSize([size]),如果指定了 size 则设置系统树布局的节点尺寸为指定的数值二元数组,表示为 [width, height] 并返回当前树布局。如果没有指定 size 则返回当前节点尺寸,默认为 null。如果返回的尺寸为 null 则表示使用 layout size 来自动计算节点大小。当指定了节点尺寸时,根节点的位置总是位于 ⟨0, 0⟩。
  • d3.tree().separation([separation]),定义邻居节点的距离。如果指定了 seperation, 则设置间隔访问器为指定的函数并返回当前树布局。如果没有指定 seperation 则返回当前的间隔访问器,默认为:
function separation(a, b) {
  return a.parent == b.parent ? 1 : 2;
}
  • 一种更适合于径向布局的变体,可以按比例缩小半径差距:
function separation(a, b) {
  return (a.parent == b.parent ? 1 : 2) / a.depth;
}
  • 间隔访问器用来设置相邻的两个节点之间的间隔。

绘制

1. 数据准备

//定义边界  
  var marge = {top:50, bottom:0, left:10, right:0}; 
  var svg = d3.select("svg");
  var width = svg.attr("width");
  var height = svg.attr("height");
  var g = svg.append("g")
    .attr("transform","translate("+marge.top+","+marge.left+")");
  var scale = svg.append("g")
    .attr("transform","translate("+marge.top+","+marge.left+")");
  //数据
  var dataset = {
    name:"中国",
    children:[
      {
        name:"浙江",
        children:[
          {name:"杭州" ,value:100},
          {name:"宁波",value:100},
              {name:"温州",value:100},
              {name:"绍兴",value:100}
        ]
      },
      {
        name:"广西",
        children:[
          {
            name:"桂林",
            children:[
              {name:"秀峰区",value:100},
                    {name:"叠彩区",value:100},
                    {name:"象山区",value:100},
                    {name:"七星区",value:100}
            ]
          },
          {name:"南宁",value:100},
              {name:"柳州",value:100},
              {name:"防城港",value:100}
        ]
      },
      {
        name:"黑龙江",
        children:[
          {name:"哈尔滨",value:100},
              {name:"齐齐哈尔",value:100},
              {name:"牡丹江",value:100},
              {name:"大庆",value:100}
        ]
      },
      {
        name:"新疆" , 
          children:
          [
                {name:"乌鲁木齐"},
                {name:"克拉玛依"},
                {name:"吐鲁番"},
                {name:"哈密"}
          ]
      }
    ]
  };

2. 创建层级布局

var hierarchyData = d3.hierarchy(dataset)
  .sum(function(d){
  return d.value;
});

3. 创建一个树状图

//创建一个树状图
var tree = d3.tree()
.size([width-400,height-200])
.separation(function(a,b){
return (a.parent==b.parent?1:2)/a.depth;
}) 

4. 初始化树状图,也就是传入数据,并得到绘制树基本数据

var treeData = tree(hierarchyData);
var nodes = treeData.descendants();
var links = treeData.links();

5. 创建一个贝塞尔生成曲线生成器

var Bézier_curve_generator = d3.linkHorizontal()
        .x(function(d) { return d.y; })
        .y(function(d) { return d.x; });

6. 绘制边

//绘制边
g.append("g")
.selectAll("path")
.data(links)
.enter()
.append("path")
.attr("d",function(d){
var start = {x:d.source.x,y:d.source.y};
var end = {x:d.target.x,y:d.target.y};
return Bézier_curve_generator({source:start,target:end});
        })
        .attr("fill","none")
        .attr("stroke","yellow")
        .attr("stroke-width",1);

注意,attr(“d”,function(d)这个函数第二个参数的格式要求。

7. 常规:建立用来放在每个节点和对应文字的分组

var gs = g.append("g")
        .selectAll("g")
        .data(nodes)
        .enter()
        .append("g")
        .attr("transform",function(d){
          var cx = d.x;
          var cy= d.y;
          return "translate("+cy+","+cx+")";
        });

8. 绘制节点和文字

//绘制节点
  gs.append("circle")
  .attr("r",6)
  .attr("fill","white")
  .attr("stroke","blue")
  .attr("stroke-width",1);
  //文字
  gs.append("text")
  .attr("x",function(d){
    return d.children?-40:8;//如果某节点有子节点,则对应的文字前移
  })
  .attr("y",-5)
  .attr("dy",10)
  .text(function(d){
    return d.data.name;
  })

完整代码

<body>
    <svg width="1000" height="1000"></svg>
    <script>
      var marge = {top:60,bottom:60,left:60,right:60}
      var svg = d3.select("svg")
      var width = svg.attr("width")
      var height = svg.attr("height")
      var g = svg.append("g").attr("transform","translate("+marge.top+","+marge.left+")");
      var scale = svg.append("g")
      //1. 准备数据
      var dataset = {
    name:"中国",
    children:[
      {
        name:"浙江",
        children:[
          {name:"杭州" ,value:100},
          {name:"宁波",value:100},
              {name:"温州",value:100},
              {name:"绍兴",value:100}
        ]
      },
      {
        name:"广西",
        children:[
          {
            name:"桂林",
            children:[
              {name:"秀峰区",value:100},
                    {name:"叠彩区",value:100},
                    {name:"象山区",value:100},
                    {name:"七星区",value:100}
            ]
          },
          {name:"南宁",value:100},
              {name:"柳州",value:100},
              {name:"防城港",value:100}
        ]
      },
      {
        name:"黑龙江",
        children:[
          {name:"哈尔滨",value:100},
              {name:"齐齐哈尔",value:100},
              {name:"牡丹江",value:100},
              {name:"大庆",value:100}
        ]
      },
      {
        name:"新疆" , 
          children:
          [
                {name:"乌鲁木齐"},
                {name:"克拉玛依"},
                {name:"吐鲁番"},
                {name:"哈密"}
          ]
      }
    ]
  };
  //2. 创建层级布局
  var hierarchyData = d3.hierarchy(dataset)
    .sum(function(d){
    return d.value;
  });
  //3. 创建一个树状图
  var tree = d3.tree()
  .size([width-400,height-200])
  .separation(function(a,b){
  return (a.parent==b.parent?1:2)/a.depth;
  }) 
  //4. 初始化树状图,也就是传入数据,并得到绘制树基本数据
  var treeData = tree(hierarchyData);
  var nodes = treeData.descendants();
  var links = treeData.links();
  //5. 创建一个贝塞尔生成曲线生成器
  var Bézier_curve_generator = d3.linkHorizontal()
        .x(function(d) { return d.y; })
        .y(function(d) { return d.x; });
  //6. 绘制边
  g.append("g")
    .selectAll("path")
    .data(links)
    .enter()
    .append("path")
    .attr("d",function(d){
var start = {x:d.source.x,y:d.source.y};
var end = {x:d.target.x,y:d.target.y};
return Bézier_curve_generator({source:start,target:end});
        })
        .attr("fill","none")
        .attr("stroke","yellow")
        .attr("stroke-width",1);
  //7. 常规:建立用来放在每个节点和对应文字的分组<g>
  var gs = g.append("g")
    .selectAll("g")
    .data(nodes)
    .enter()
    .append("g")
    .attr("transform",function(d){
      var cx = d.x;
      var cy= d.y;
      return "translate("+cy+","+cx+")";
    });
  //8. 绘制节点和文字
  gs.append("circle")
  .attr("r",6)
  .attr("fill","white")
  .attr("stroke","blue")
  .attr("stroke-width",1);
  //文字
  gs.append("text")
  .attr("x",function(d){
    return d.children?-40:8;//如果某节点有子节点,则对应的文字前移
  })
  .attr("y",-5)
  .attr("dy",10)
  .text(function(d){
    return d.data.name;
  })
</script>
</body>


相关文章
|
6月前
|
JavaScript 前端开发 安全
【逆向】Python 调用 JS 代码实战:使用 pyexecjs 与 Node.js 无缝衔接
本文介绍了如何使用 Python 的轻量级库 `pyexecjs` 调用 JavaScript 代码,并结合 Node.js 实现完整的执行流程。内容涵盖环境搭建、基本使用、常见问题解决方案及爬虫逆向分析中的实战技巧,帮助开发者在 Python 中高效处理 JS 逻辑。
|
8月前
|
JavaScript 前端开发 算法
流量分发代码实战|学会用JS控制用户访问路径
流量分发工具(Traffic Distributor),又称跳转器或负载均衡器,可通过JavaScript按预设规则将用户随机引导至不同网站,适用于SEO优化、广告投放、A/B测试等场景。本文分享一段不到百行的JS代码,实现智能、隐蔽的流量控制,并附完整示例与算法解析。
219 1
|
9月前
|
JavaScript 前端开发
怀孕b超单子在线制作,p图一键生成怀孕,JS代码装逼娱乐
模拟B超单的视觉效果,包含随机生成的胎儿图像、医疗文本信息和医院标志。请注意这仅用于前端开发学习
|
11月前
|
存储 JavaScript 前端开发
在NodeJS中使用npm包进行JS代码的混淆加密
总的来说,使用“javascript-obfuscator”包可以帮助我们在Node.js中轻松地混淆JavaScript代码。通过合理的配置,我们可以使混淆后的代码更难以理解,从而提高代码的保密性。
1064 9
|
9月前
|
JavaScript
JS代码的一些常用优化写法
JS代码的一些常用优化写法
157 0
|
12月前
|
前端开发 JavaScript
【Javascript系列】Terser除了压缩代码之外,还有优化代码的功能
Terser 是一款广泛应用于前端开发的 JavaScript 解析器和压缩工具,常被视为 Uglify-es 的替代品。它不仅能高效压缩代码体积,还能优化代码逻辑,提升可靠性。例如,在调试中发现,Terser 压缩后的代码对删除功能确认框逻辑进行了优化。常用参数包括 `compress`(启用压缩)、`mangle`(变量名混淆)和 `output`(输出配置)。更多高级用法可参考官方文档。
832 11
|
人工智能 程序员 UED
【01】完成新年倒计时页面-蛇年新年快乐倒计时领取礼物放烟花html代码优雅草科技央千澈写采用html5+div+CSS+JavaScript-优雅草卓伊凡-做一条关于新年的代码分享给你们-为了C站的分拼一下子
【01】完成新年倒计时页面-蛇年新年快乐倒计时领取礼物放烟花html代码优雅草科技央千澈写采用html5+div+CSS+JavaScript-优雅草卓伊凡-做一条关于新年的代码分享给你们-为了C站的分拼一下子
724 21
【01】完成新年倒计时页面-蛇年新年快乐倒计时领取礼物放烟花html代码优雅草科技央千澈写采用html5+div+CSS+JavaScript-优雅草卓伊凡-做一条关于新年的代码分享给你们-为了C站的分拼一下子
|
前端开发 JavaScript
【02】v1.0.1更新增加倒计时完成后的放烟花页面-优化播放器-优化结构目录-蛇年新年快乐倒计时领取礼物放烟花html代码优雅草科技央千澈写采用html5+div+CSS+JavaScript-优雅草卓伊凡-做一条关于新年的代码分享给你们-为了C站的分拼一下子
【02】v1.0.1更新增加倒计时完成后的放烟花页面-优化播放器-优化结构目录-蛇年新年快乐倒计时领取礼物放烟花html代码优雅草科技央千澈写采用html5+div+CSS+JavaScript-优雅草卓伊凡-做一条关于新年的代码分享给你们-为了C站的分拼一下子
492 14
【02】v1.0.1更新增加倒计时完成后的放烟花页面-优化播放器-优化结构目录-蛇年新年快乐倒计时领取礼物放烟花html代码优雅草科技央千澈写采用html5+div+CSS+JavaScript-优雅草卓伊凡-做一条关于新年的代码分享给你们-为了C站的分拼一下子
|
12月前
|
JavaScript 前端开发 算法
JavaScript 中通过Array.sort() 实现多字段排序、排序稳定性、随机排序洗牌算法、优化排序性能,JS中排序算法的使用详解(附实际应用代码)
Array.sort() 是一个功能强大的方法,通过自定义的比较函数,可以处理各种复杂的排序逻辑。无论是简单的数字排序,还是多字段、嵌套对象、分组排序等高级应用,Array.sort() 都能胜任。同时,通过性能优化技巧(如映射排序)和结合其他数组方法(如 reduce),Array.sort() 可以用来实现高效的数据处理逻辑。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
12月前
|
JavaScript 前端开发 API
JavaScript中通过array.map()实现数据转换、创建派生数组、异步数据流处理、复杂API请求、DOM操作、搜索和过滤等,array.map()的使用详解(附实际应用代码)
array.map()可以用来数据转换、创建派生数组、应用函数、链式调用、异步数据流处理、复杂API请求梳理、提供DOM操作、用来搜索和过滤等,比for好用太多了,主要是写法简单,并且非常直观,并且能提升代码的可读性,也就提升了Long Term代码的可维护性。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~

热门文章

最新文章