Bootstrap

antv/g6 绘制生态图-新手入门

实现效果如下图:

 API文档:图配置 G6.Graph(cfg) | G6

参考样例:自定义树图 | G6

 坐标转换:坐标转换

一、安装 & 引用

1. 通过npm包引入

npm install --save @antv/g6

   在需要使用的文件中直接引入G6即可

import G6 from "@antv/g6"

2. 通过CDN形式引入

// version <= 3.2
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-{$version}/build/g6.js"></script>

// version >= 3.3
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/{$version}/dist/g6.min.js"></script>

二、数据结构如下:

const mockData = {
                id: 'g1',
                name: '我是科目我是科目我是科目我是科目我是科目我是科目目目目目',
                dataType: 'root',
                amount: 123123213123.123123,
                children: [
                    {
                        id: 'g12',
                        name: 'Deal with LONG label LONG label LONG label LONG label',
                        amount: 123123213123.123123,
                        children: [
                            {
                                id: 'g121',
                                name: 'Name3',
                                collapsed: true,
                                amount: 123123213123.123123,
                                children: [
                                    {
                                        id: 'g1211',
                                        name: 'Name4',
                                        amount: 123123213123.123123,
                                        children: [],
                                    },
                                ],
                            },
                            {
                                id: 'g122',
                                name: 'Name5',
                                collapsed: true,
                                amount: 2.34,
                                children: [
                                    {
                                        id: 'g1221',
                                        name: 'Name6',
                                        amount: 0.123123,
                                        children: [
                                            {
                                                id: 'g12211',
                                                name: 'Name6-1',
                                                amount: 123123,
                                                children: [],
                                            },
                                        ],
                                    },
                                    {
                                        id: 'g1222',
                                        name: 'Name7',
                                        amount: 123123213123.123123,
                                        children: [],
                                    },
                                ],
                            },
                            {
                                id: 'g123',
                                name: 'Name8',
                                collapsed: true,
                                amount: 12,
                                children: [
                                    {
                                        id: 'g1231',
                                        name: 'Name8-1',
                                        amount: 123123213123.123123,
                                        children: [],
                                    },
                                ],
                            },
                        ],
                    },
                    {
                        id: 'g13',
                        name: 'Name9',
                        amount: 123123213123.123123,
                        children: [
                            {
                                id: 'g131',
                                name: 'Name10',
                                amount: 12313123.123123,
                                children: [],
                            },
                            {
                                id: 'g132',
                                name: 'Name11',
                                amount: 123123213123.123123,
                                children: [],
                            },
                        ],
                    },
                    {
                        id: 'g14',
                        name: 'Name12',
                        amount: 123123213123.123123,
                        children: [],
                    },
                ],
            };

完整代码如下:

<template>
    <div id="container"></div>
</template>
  
<script>
import G6 from '@antv/g6';

export default {
    name: 'HelloWorld',
    props: {
        msg: String
    },
    created() { },
    mounted() {
        this.initAntv();
    },
    methods: {
        // 初始化
        initAntv() {

            //  组件props
            const props = {
                data: mockData,
                config: {
                    padding: [20, 50],
                    // defaultLevel: 3,
                    defaultZoom: 0.8,
                    modes: { default: ['zoom-canvas', 'drag-canvas'] },
                },
            };

            const container = document.getElementById('container');
            const width = container.scrollWidth;
            const height = container.scrollHeight || 500;

            // 默认配置
            const defaultConfig = {
                width,
                height,
                modes: {
                    default: ['zoom-canvas', 'drag-canvas'],
                },
                fitView: true,
                animate: true,
                minZoom: 0.5,
                maxZoom: 1.5,
                linkCenter: false,
                defaultNode: {  // 默认节点
                    type: 'flow-rect',
                },
                defaultEdge: {  // 默认边
                    type: 'cubic-horizontal',
                    style: {
                        stroke: 'rgb(93, 162, 216)',
                    },
                },
                layout: {  // layout 布局配置
                    type: 'mindmap', 
                    direction: 'V',
                    dropCap: false,
                    rankSep: 350,
                    nodeSep: 100,
                    getHeight: () => {
                        return 60;
                    },
                    // 节点横向间距的回调函数
                    getHGap: function getHGap() {
                        return 150;
                    }
                },
            };

            // 自定义节点
            const registerFn = () => {
                /**
                 * 自定义节点
                 */
                G6.registerNode(
                    'flow-rect',
                    {
                        shapeType: 'flow-rect',
                        draw(cfg, group) {
                            const {
                                name = '',
                                collapsed,
                                dataType,
                                amount,
                            } = cfg;

                            const config = {
                                basicColor: 'rgb(66, 151, 215)',  // 左边粗线的颜色
                                fontColor: 'rgb(74, 177, 241)',  // 字体颜色
                                borderColor: 'rgb(169, 217, 252)', // 边框颜色
                                bgColor: 'rgb(230, 240, 250)',  // 矩形背景色
                            };

                            const rectConfig = {
                                width: 243,
                                height: 64,
                                lineWidth: 1,
                                fontSize: 12,
                                opacity: 1,
                                fill: config.bgColor,
                                stroke: config.borderColor,
                                radius: 2,
                                cursor: 'pointer',
                            };

                            const nodeOrigin = {
                                x: -rectConfig.width / 2,
                                y: -rectConfig.height / 2,
                            };

                            const textConfig = {
                                textAlign: 'left',
                                textBaseline: 'bottom',
                            };

                            if (dataType != 'root') {
                                /* 左边的小圆点 */
                                group.addShape('circle', {
                                    attrs: {
                                        x: nodeOrigin.x - 0,
                                        y: 0,
                                        r: 6,
                                        fill: config.basicColor,
                                    },
                                    name: 'left-dot-shape',
                                });
                            }
                            // 外层矩形
                            const rect = group.addShape('rect', {
                                attrs: {
                                    x: nodeOrigin.x,
                                    y: nodeOrigin.y,
                                    ...rectConfig,
                                },
                            });

                            /* 左边的粗线 */
                            group.addShape('rect', {
                                attrs: {
                                    x: nodeOrigin.x,
                                    y: nodeOrigin.y,
                                    width: 3,
                                    height: rectConfig.height,
                                    fill: config.basicColor,
                                    radius: 1.5,
                                },
                                name: 'left-border-shape',
                            });




                            const rectBBox = rect.getBBox();

                            // 科目
                            group.addShape('text', {
                                attrs: {
                                    ...textConfig,
                                    x: 12 + nodeOrigin.x,
                                    y: 20 + nodeOrigin.y,
                                    text: name.length > 16 ? name.substr(0, 16) + '...' : name,
                                    fontSize: 12,
                                    opacity: 0.85,
                                    fill: config.fontColor,
                                    cursor: 'pointer',
                                },
                                name: 'name-shape',
                            });

                            if (amount) {
                                // 金额-矩形
                                const amountRect = group.addShape('rect', {
                                    attrs: {
                                        fill: '#fff',
                                        radius: 2,
                                        stroke: config.borderColor,
                                        cursor: 'pointer', // 需要调整
                                    },
                                    name: 'amount-container-shape',
                                })
                                // 金额
                                const amountText = group.addShape('text', {
                                    attrs: {
                                        ...textConfig,
                                        x: rectBBox.maxX - 10,
                                        y: rectBBox.maxY - 10,
                                        text: `${amount}`,
                                        fontSize: 12,
                                        textAlign: 'right',
                                        fill: '#000',
                                    },
                                    name: 'amount-text-shape',
                                });

                                const amountBBox = amountText.getBBox(); // 获取元素的包围盒
                                /* amountBBox */
                                amountRect.attr({
                                    x: rectBBox.maxX - amountBBox.width - 15,
                                    y: rectBBox.maxY - 27,
                                    width: amountBBox.width + 10,
                                    height: amountBBox.height + 10,
                                });
                            }

                            // collapse rect
                            if (cfg.children && cfg.children.length) {
                                group.addShape('circle', {
                                    attrs: {
                                        x: rectConfig.width / 2 + 8.5,
                                        y: 0,
                                        r: 8,
                                        width: 16,
                                        height: 16,
                                        stroke: 'rgb(74, 177, 241)',
                                        cursor: 'pointer',
                                        fill: '#fff',
                                    },
                                    name: 'collapse-back',
                                    modelId: cfg.id,
                                });

                                // collpase text
                                group.addShape('text', {
                                    attrs: {
                                        x: rectConfig.width / 2 + 8.5,
                                        y: 1,
                                        textAlign: 'center',
                                        textBaseline: 'middle',
                                        text: collapsed ? '+' : '-',
                                        fontSize: 16,
                                        cursor: 'pointer',
                                        fill: 'rgb(74, 177, 241)',
                                    },
                                    name: 'collapse-text',
                                    modelId: cfg.id,
                                });
                            }
                            return rect;
                        },
                        update(item) {
                            // const { level, name } = cfg;
                            // console.log("update生命周期函数中的cfg========", cfg)
                            const group = item.getContainer();
                            let mask = group.find(ele => ele.get('name') === 'mask-shape');
                            let maskLabel = group.find(ele => ele.get('name') === 'mask-label-shape');

                            group.get('children').forEach(child => {
                                if (child.get('name')?.includes('collapse')) return;
                                child.show();
                            })
                            mask?.animate({ opacity: 0 }, {
                                duration: 200,
                                callback: () => mask.hide()
                            });
                            maskLabel?.animate({ opacity: 0 }, {
                                duration: 200,
                                callback: () => maskLabel.hide()
                            });

                        },
                        setState(name, value, item) {
                            if (name === 'collapse') {
                                const group = item.getContainer();
                                const collapseText = group.find((e) => e.get('name') === 'collapse-text');
                                if (collapseText) {
                                    if (!value) {
                                        collapseText.attr({
                                            text: '-',
                                        });
                                    } else {
                                        collapseText.attr({
                                            text: '+',
                                        });
                                    }
                                }
                            }
                        },
                        getAnchorPoints() {
                            return [
                                [0, 0.5],
                                [1, 0.5],
                            ];
                        },
                    },
                    'rect',
                );
            };

            registerFn();

            const { data } = props;
            let graph = null;

            const initGraph = (data) => {
                if (!data) {
                    return;
                }
                const { onInit, config } = props;
                const tooltip = new G6.Tooltip({
                    // offsetX and offsetY include the padding of the parent container
                    offsetX: 20,
                    offsetY: 30,
                    // 允许出现 tooltip 的 item 类型
                    itemTypes: ['node'],
                    // custom the tooltip's content
                    // 自定义 tooltip 内容
                    getContent: (e) => {
                        const outDiv = document.createElement('div');
                        outDiv.style.textAlign = 'left';
                        const nodeName = e.item.getModel().name;
                        let formatedNodeName = '';
                        for (let i = 0; i < nodeName.length; i++) {
                            formatedNodeName = `${formatedNodeName}${nodeName[i]}`;
                            if (i !== 0 && i % 20 === 0) formatedNodeName = `${formatedNodeName}<br/>`;
                        }
                        outDiv.innerHTML = `${formatedNodeName}`;
                        return outDiv;
                    },
                    shouldBegin: (e) => {
                        // if (e.target.get('name') === 'name-shape' || e.target.get('name') === 'mask-label-shape') return true;
                        const nodeName = e.item.getModel().name;
                        if (nodeName.length > 24) {
                            return true
                        } else {
                            return false;
                        }
                    },
                });
                graph = new G6.TreeGraph({
                    container: 'container',
                    ...defaultConfig,
                    ...config,
                    plugins: [tooltip],
                });
                if (typeof onInit === 'function') {
                    onInit(graph);
                }
                graph.data(data);
                graph.render();

                const handleCollapse = (e) => {
                    const target = e.target;
                    const id = target.get('modelId');
                    const item = graph.findById(id);
                    const nodeModel = item.getModel();
                    nodeModel.collapsed = !nodeModel.collapsed;
                    graph.layout();
                    graph.setItemState(item, 'collapse', nodeModel.collapsed);
                };
                graph.on('collapse-text:click', (e) => {
                    handleCollapse(e);
                });
                graph.on('collapse-back:click', (e) => {
                    handleCollapse(e);
                });
            };

            initGraph(data);

            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>
  
  <!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped></style>
  

问题:

当我们按照以上操作绘制完图之后,发现画布中的内容并非居中展示,设置 fitView: true 并没有起作用,有可能是因为布局原因,有定位的设置可能会影响画布的中心坐标,有了解决问题的思路 ,我们就开始着手试试把。增加以下代码:

// 设置画布居中
setCenetr(graph) {
    const con_width = document.getElementById("container").clientWidth;
    const con_Height = document.getElementById("container").clientHeight;

    // 重新计算画布中心的坐标
    const centerX = con_width / 2 - 60;
    const centerY = con_Height / 2 - 60;
    // 以 x, y 为坐标中心,进行缩放
    graph.zoomTo(0.7, {
        x: centerX,
        y: centerY
    })
}

以上方法需在 graph.render() 之前执行。

友情提示:新手小白入门的话最好是先找个和自己需求相似案例看看,然后开始照猫画虎,画着画着慢慢就能就看明白了,当然 大佬略过哈 ~  多看看官方的文档,写的很详细。

;