在react中通过@antv/g6实现树形图,包括自定义连接线、弹窗、展开/收起、配置项说明
const flowLine = {
draw(cfg: any, group: any) {
const startPoint = cfg.startPoint;
const endPoint = cfg.endPoint;
const sourceModel = cfg?.sourceNode?._cfg?.model;
const targetModel = cfg?.targetNode?._cfg?.model;
const path = [
['M', startPoint.x, startPoint.y - 20],
['L', startPoint.x, (startPoint.y + endPoint.y) / 2 + 8],
['L', endPoint.x, (startPoint.y + endPoint.y) / 2 + 8],
['L', endPoint.x, endPoint.y],
];
const sourceSelected = sourceModel?.isAbnormal;
const targetSelected = targetModel?.isAbnormal;
const isSelected = !!sourceModel?.parentMetricNodeId
? targetSelected && sourceSelected
: targetSelected;
const shape = group.addShape('path', {
attrs: {
stroke: isSelected ? 'red' : '#cacaca',
path,
},
});
return shape;
},
}
import React from 'react';
import { Rect, Text, Group } from '@antv/g6-react-node';
import { nullIf, toFixed, toPercentage } from '@/utils/tools';
interface Props {
cfg: any;
}
export default function ParentChild({ cfg }: Props) {
const { name, value, cycleValueDiff = 0, onClick } = cfg;
return (
{nullIf(name)}
{nullIf(value ? parseFloat(value) : null, { render: toFixed })}
{`环比:`}
{nullIf(cycleValueDiff, { render: toPercentage })?.toString()}
);
}
const defaultStateStyles = {
hover: {
stroke: '#1890ff',
lineWidth: 2,
},
};
const defaultLayout = {
type: 'compactBox',
direction: 'TB',
getId: function getId(d: any) {
return d.id;
},
getHeight: function getHeight() {
return 196;
},
getWidth: function getWidth() {
return 280;
},
getVGap: function getVGap() {
return 8;
},
getHGap: function getHGap() {
return 24;
},
};
const tooltip = new G6.Tooltip({
fixToNode: [0, 0],
offsetX: 42,
offsetY: -16,
className: 'g6-tooltip',
itemTypes: ['node'],
shouldBegin: (e: any) => {
return !!e?.item?._cfg?.model?.description;
},
getContent(e: any) {
const model = e?.item?.getModel();
return `${model?.name}${model?.description || ''}`;
},
});
function getTreeGraphConfig({ width, height }: any) {
return {
container: 'container',
width,
height,
linkCenter: true,
// fitCenter: true,
fitView: true,
preventOverlap: true,
plugins: [tooltip],
modes: {
default: [
{
type: 'collapse-expand',
trigger: 'click',
onChange: function onChange(item: any, collapsed: any) {
const cur = item.get('model');
cur.collapsed = collapsed;
return true;
},
shouldBegin: (e: any) => {
// 若当前操作的节点 type 不为 'marker',则不发生 collapse-expand
if (e?.target?.get('type') != 'marker') return false;
return true;
},
},
// 开启选中之后高亮相邻
// {
// type: 'activate-relations',
// resetSelected: true
// },
// 开启之后画布滚动
// {
// type: 'scroll-canvas',
// zoomKey: 'ctrl'
// },
// 缩放
// 'zoom-canvas',
// 拖拽
// 'drag-canvas',
// 'scroll-canvas',
// 'drag-node',
],
},
// renderer: "svg",
defaultNode: {
type: 'node-child',
size: [280, 156],
},
defaultEdge: {
type: 'flow-line',
},
nodeStateStyles: defaultStateStyles,
edgeStateStyles: defaultStateStyles,
layout: defaultLayout,
};
}
const graphRef = useRef();
useEffect(() => {
G6.registerNode('node-child', createNodeFromReact( ));
G6.registerEdge('flow-line', flowLine);
const container: any = document.getElementById('container');
const width = container.scrollWidth;
const height = container.scrollHeight || window.innerHeight;
const cfg = getTreeGraphCofig({ width, height });
const graph = new G6.TreeGraph(cfg);
graphRef.current = graph;
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);
};
}, []);
//加载渲染数据
const graph = graphRef.current;
getIndicatorsTree(defaultParams).then((res) => {
if (res?.data?.length > 0) {
graph.data(res?.data?.[0]);
graph.setMaxZoom(1);
graph.render();
graph.fitView([8, 16], { onlyOutOfViewPort: true, ratioRule: 'min' });
// 添加事件
appenAutoShapeListener(graph);
}
});
参考:
[官网:@antv/g6]
[官网:React中使用@antv/g6]
[官网:使用dom自定义节点]
[官网:React中自定义节点]
G6 的节点/边事件不支持 DOM 类型的图形。如果需要为 DOM 节点绑定事件,请使用原生 DOM 事件