Bootstrap

G6绘制树形图(自定义节点、自定义边、自定义布局)

1 设计节点

在 registerNode 中定义所有的节点

G6.registerNode('tree-node', {
	drawShape: function drawShape(cfg, group) {
	定义图中需要的节点
	}
}, 'single-node',);

为了使用内置的布局方式,选择参数为 ‘tree-node’ 树节点类型,数据格式可以存在children子节点,效果自动生成子树
cfg 可以拿到数据,如cfg.id、cfg.name

1.1 定义节点和文本

使用 group.addShape(‘rect’, {}) 定义节点 rect
配置参数:https://antv-g6.gitee.io/zh/docs/api/shapeProperties/#fill

        // 定义节点 rect 
        const rect = group.addShape('rect', {  // 'rect'表示矩形图形
          attrs: {
            // 节点定义参数:颜色、阴影...
          },
          name: 'rect-shape',   // 为这个节点起名字 不过没有使用过这个名字
        });

使用 group.addShape(‘text’, {}) 定义文本 text

		// 定义文本text
        const text = group.addShape('text', {  // 'text'表示文本
          attrs: {
            // 参数:颜色、文字...
          },
          name: 'text-shape',
        });

节点和文字生成后,再定义他们的相对位置
参考官网定义复杂图样式的方式:https://antv-g6.gitee.io/zh/examples/tree/customItemTree#customTree
使用 .getBBox() 获得该文本的盒子bbox,使用文本盒子的相对位置后面的位置坐标

        const bbox = text.getBBox();   // 获得文本的盒子
        // 设置rect 节点的位置
        rect.attr({
          x: -bbox.width / 2 - 5,   // x坐标
          y: -bbox.height,			// y坐标
          width: bbox.width +  12 ,	 // 宽
          height: bbox.height + 8,   // 高
        });
        // 设置text文本的位置
        text.attr({
          x: -bbox.width / 2,
          y: -bbox.height / 2 + 3,
        })

效果如下
在这里插入图片描述

1.2 增加节点

如果想为节点再增加一个小节点,并且位置随着大节点移动,如图
在这里插入图片描述
新增节点和文本 rect2 text2

rect2 = group.addShape('rect', {
        attrs: {
                // 参数
        },
        name: 'rect-shape2',
});
const text2 = group.addShape('text', {
        attrs: {
                // 参数
        },
        name: 'text-shape2',
});

为rect2 text2设置坐标,以bbox作为参考位置

              // 设置坐标轴和宽高
              rect2.attr({
                  x: -bbox.width / 2 - 24,
                  y: -bbox.height / 2 - 1,
                  width: 14,
                  height: 10,
              });
              text2.attr({
                  x: -bbox.width / 2 - 23,
                  y: -bbox.height / 2 + 4,
              })

1.3 自定义节点样式

			roup.addShape('dom', {
                attrs: {
                  x: -bbox.width / 2 - 24 + 14,   // 即:rect的坐标 + rect的宽 
                  y: -bbox.height / 2 - 1,
                  width: 10,
                  height: 10,
                  html: `
                  <div style="border: 5px solid red;">
                  	自定义dom
                  </div>
                    `,
                },
                draggable: true,
              });

使用自定义dom,在 new G6.TreeGraph中 需要设置

renderer : 'svg',   // 奇怪的是设置之后原来节点的布局有些影响

2 树图配置

2.1 允许使用自定义dom节点

renderer : 'svg', 

2.2 内置行为

https://antv-g6.gitee.io/zh/docs/manual/middle/states/defaultBehavior#%E5%86%85%E7%BD%AE-behavior

modes: {
          default: [
            {
              type: 'collapse-expand',
              onChange: function onChange(item, collapsed) {
                const data = item.get('model');
                graph.updateItem(item, {
                  collapsed,
                });
                data.collapsed = collapsed;
                return true;
              },
            },
            'drag-canvas',    // 允许拖动
            'zoom-canvas',	 // ....
          ],
        },

自定义边

defaultEdge: {
          type: 'cubic-horizontal',
          style: {
            stroke: 'red'   //红色
          },
        },

layout布局

https://antv-g6.gitee.io/zh/docs/manual/middle/layout/tree-graph-layout

layout: {
            type: 'indented',
            direction: 'LR',   // 节点从左向右分布
            dropCap: false,
            indent: 190,
            getHeight: () => {
              return 13;
            },
            getVGap: function getVGap () {
              return 10;
            },
        },

demo

<template>
    <div class="main-content-box">
      <div id="container"></div>
    </div>
</template>
  
  <script>
  import G6 from '@antv/g6';

  export default {
    name: 'multTagsSec',
    data () {
      return {
        gDatas:{
                "id": "1",
                "name": "storehouse A",
                "children": [
                  {
                    "id": "2",
                    "name": "B",
                    "percentage": "60%",
                    "children": [
                      {
                        "id": "3",
                        "name": "storehouse C",
                        "percentage": "80%",
                        "children": [
                          {
                            "name": "storehouse C",
                            "percentage": "80%",
                            "children": [
                              {
                                "name": "D",
                                "percentage": "20%"
                              },
                              {
                                "name": "storehouselllllll C",
                                "percentage": "20%"
                              }
                            ]
                          },
                          {
                            "name": "storehouse D",
                            "percentage": "20%"
                          }
                        ]
                      },
                      {
                        "name": "storehouse D",
                        "percentage": "20%"
                      }
                    ]
                  },
                  {
                    "name": "storehouse C",
                    "percentage": "100%"
                  },
                  {
                    "name": "storehouse B",
                    "percentage": "20%"
                  },
                  {
                    "name": "storehouse C",
                    "percentage": "20%"
                  },
                  {
                    "name": "storehouse C",
                    "percentage": "20%",
                    "children": [
                              {
                                "name": "D",
                                "percentage": "20%"
                              },
                              {
                                "name": "storehouse A",
                                "percentage": "20%"
                                
                              }
                            ]
                  }
                ]
              }
  
      }
    },
    mounted() {
      this.getInit();
    },
    methods: {
      getInit () {
        // var mycfg = null;
        G6.registerNode('tree-node', {
          drawShape: function drawShape(cfg, group) {
            // console.log(cfg)
            
            // --------------------标签内容节点----------------------
            var hasChildren = cfg.children && cfg.children.length > 0;   // 是否有孩子节点
            var strokeColor =  hasChildren == true ? 'red' : null     // 有孩子 为红色
            // 节点设置 
            const rect = group.addShape('rect', {
              attrs: {
                fill: '#fff',
                stroke: strokeColor,  // 边框颜色
                lineWidth: 1,       // 边框粗细
                radius: 2,
                shadowBlur: 15,
                shadowColor: '#666',
                // shadowOffsetX: 2,
                // shadowOffsetY: 2
              },
              name: 'rect-shape',
            });
            // 文本设置
            const text = group.addShape('text', {
              attrs: {
                text: cfg.name,       // 赋值name属性
                fontFamily: 'normal',
                fontSize: 11,
                fontWeight: 800,
                x: 0,
                y: 0,
                textAlign: 'left',
                textBaseline: 'middle',
                fill: '#666'
              },
              name: 'text-shape',
            });
            
            const bbox = text.getBBox();   // 获得文本的盒子 之后的两个节点的xy轴坐标参考bbox
            //const minbbox = rect.getBBox();
            // 设置 rect方框和text文本 的 x y坐标轴
            rect.attr({
              x: -bbox.width / 2 - 5,
              y: -bbox.height,
              // width: bbox.width + (hasChildren ? 20 : 12),
              width: bbox.width +  12 ,
              height: bbox.height + 8,
            });
            text.attr({
              x: -bbox.width / 2,
              y: -bbox.height / 2 + 3,
            })
            
            // -----------百分比节点----------
            var hasPercentage = cfg.percentage;
            var rect2 = 0;
            if(hasPercentage){
                // 节点设置 2
                rect2 = group.addShape('rect', {
                attrs: {
                    fill: '#4682B4',
                    stroke: '',  // 边框颜色
                    lineWidth: 0,       // 边框粗细
                    shadowBlur: 0,
                    shadowColor: '',
                },
                name: 'rect-shape2',
              });
              // 文本设置 2
              const text2 = group.addShape('text', {
              attrs: {
                  text: cfg.percentage,       // 赋值name属性
                  fontFamily: 'normal',
                  fontSize: 5,
                  fontWeight: 500,
                  textAlign: 'left',
                  textBaseline: 'middle',
                  fill: 'white'
                },
                name: 'text-shape2',
              });
              // 设置坐标轴和宽高
              rect2.attr({
                  x: -bbox.width / 2 - 24,
                  y: -bbox.height / 2 - 1,
                  width: 14,
                  height: 10,
              });
              text2.attr({
                  x: -bbox.width / 2 - 23,
                  y: -bbox.height / 2 + 4,
              })
              // -------连接两个节点的小节点----------
              // const rect3 = group.addShape('rect', {
              //     attrs: {
              //         fill: '#00BFFF',
              //         stroke: '',  // 边框颜色
              //         lineWidth: 0,       // 边框粗细
              //         shadowBlur: 0,
              //         shadowColor: '',
              //     },
              //     name: 'rect-shape3',
              // });
              // rect3.attr({
              //     x: -bbox.width / 2 - 24 + 14,   // 即:rect的坐标 + rect的宽 
              //     y: -bbox.height / 4 + 1,
              //     width: 4,
              //     height: 4
              // });
              // -------连接两个节点的小节点 三角形----------
              // 需要设置svg才能使用
              group.addShape('dom', {
                attrs: {
                  x: -bbox.width / 2 - 24 + 14,   // 即:rect的坐标 + rect的宽 
                  y: -bbox.height / 2 - 1,
                  width: 10,
                  height: 10,
                  html: `
                  <div style="border-left: 5px solid red; 
                              border-right: 5px solid transparent;
                              border-top: 5px solid transparent;
                              border-bottom: 5px solid transparent;">
                  </div>
                    `,
                },
                draggable: true,
              });
            }
            

            // 小圆圈
            if (hasChildren) {
              const redcircle = group.addShape('marker', {
                  attrs: {
                  symbol: cfg.collapsed ? G6.Marker.expand : G6.Marker.collapse,
                  // symbol: cfg.collapsed ? COLLAPSE_ICON : EXPAND_ICON,
                  stroke: 'red',
                  fill: 'red',
                  lineWidth: 1.8,
                },
                name: 'collapse-icon',
              });
              redcircle.attr({
                  x: bbox.width / 2 + 7,
                  y: -3 ,
                  r: 4,

              })
            }

          

            return rect;
          },
          update: (cfg, item) => {
            const group = item.getContainer();
            const icon = group.find((e) => e.get('name') === 'collapse-icon');
            icon.attr('symbol', cfg.collapsed ? G6.Marker.expand : G6.Marker.collapse);
          },
        },
        'single-node',
      );
  
      const container = document.getElementById('container');
      const width = container.scrollWidth;
      const height = container.scrollHeight || 500;
  
      const graph = new G6.TreeGraph({
        renderer : 'svg',     // 创建自定义DMO时定义 会报一个错 但好像不影响 
        container: 'container',
        width,
        height,
        modes: {
          default: [
            {
              type: 'collapse-expand',
              onChange: function onChange(item, collapsed) {
                const data = item.get('model');
                graph.updateItem(item, {
                  collapsed,
                });
                data.collapsed = collapsed;
                return true;
              },
            },
            // 'drag-canvas',    // 不可拖动
            'zoom-canvas',
          ],
        },
        defaultNode: {
          type: 'tree-node',
          anchorPoints: [
            [0, 0.5],
            [1, 0.5],
          ],
        },
        // 设置边的参数
        defaultEdge: {
          type: 'cubic-horizontal',
          style: {
            stroke: 'red'
          },
        },
        layout: {
            type: 'indented',
            direction: 'LR',
            dropCap: false,
            indent: 190,
            getHeight: () => {
              return 13;
            },
            getVGap: function getVGap () {
              return 10;
            },
        },
      });
  
  
      
  
      graph.data(this.gDatas);
      graph.render();
      graph.fitView();
  
      if (typeof window !== 'undefined')
        window.onresize = () => {
          if (!graph || graph.get('destroyed')) return;
          if (!container || !container.scrollWidth || !container.scrollHeight) return;
          graph.changeSize(container.scrollWidth, container.scrollHeight);
        };
      },
  
  
  
  
    }
  }
  
  </script>
  
  <style scoped>
  </style>

在这里插入图片描述

;