vue + d3 实现纵向拓扑图,子节点超过N个时换行展示



最近接手一个功能,画设备拓扑级联图,最开始使用echart.js完成,但有如下问题:

  1. 当节点过多时,节点描述文本会重叠(网上有解决方案,但效果不是很满意);
  2. 无法控制子节点超过n个时换行展示;

在尝试了很多方法效果都不好时,转向了d3.js,完成了该功能,效果图如下:

image

代码参考: vue.js中使用D3树状图异步按需加载数据绘制人物关系图

实现思路如下:

  1. 数据建模
    数据建模很重要,它直接决定后续代码的复杂度,以下是我的数据建模,仅做参考:
     // 这里位置信息我是分开定义,方便后续筛选数据,其实可以使用一个index中间用 分隔符间隔 来定义的
     {
         "name": "xx",               
         "secondIndex": 0,  // 父节点所属父节点索引
         "thirdIndex": 0,  // 节点所属父节点索引
         "index": 1,  // 节点在本层的横向位置
         "deep": 1, //节点位于第几层
         "totalWidth": 0, //节点总宽度
         "image": '../firstLevel.png', 
         "children": [],
     };
    
  2. 实现子节点超过n个时换行展示
     // 这里实现方式有两种,可以直接重置x, y坐标,也可以渲染完成后对节点进行偏移
     // 以下是对节点进行偏移的方式实现
     var nodeEnter = node.enter().append("g")
         .attr("class", "node")
         .attr("transform", function(d) {
                
             let x = source.x;
             let y = source.y0;
    
             if (d.deep === 4) {
                 // 重新计算节点坐标
                 let selfOffset = that.getFourSelfOffset(d);
                 x = selfOffset.x;
                 y = selfOffset.y;
             } 
    
             return "translate(" + x + "," + y + ")"; 
         })
         .on("click", d => that.click(d));
    
  3. 子节点换行展示后,父节点之间的间距应该缩小,重置所属父节点位置,同理也重置父节点所属父节点的位置
     // update方法中修改代码重置节点x坐标
     let isResetPosition = false;
     nodes.forEach(function(d) {
         d.x = d.x;
         if (d.deep === 3 && !isResetPosition) {     
             // 重置父节点位置
             that.resetPosition(d.parent.parent);
             isResetPosition = true;
         }
     })
    
  4. 节点描述水平居中
     nodeEnter.append("text")
         .attr("x", "0")
         .attr("y", "25")
         // 水平居中
         .attr("text-anchor", "middle")
         .attr("font-size", "12px")
         .text(function(d) { return d.name; })
         .style("fill-opacity", 1e-6);