当前位置:首页 > 编程笔记 > 正文
已解决

datahub 中血缘图的实现分析,在react中使用airbnb的visx可视化库来画有向无环图

来自网友在路上 149849提问 提问时间:2023-10-26 22:23:34阅读次数: 49

最佳答案 问答题库498位专家为你答疑解惑

背景

做大数据的项目,必不可少的是要接触到数据血缘图,它在大数据项目中有着很重要的作用。
之前在公司也做过一些案例,也看过很多友商的产品,阿里的DataWork,领英的Datahub,
datawork的血缘图使用的是 G6,自家的产品
Datahub使用的是 爱彼邻的 可视化库 visx
本篇文章就来谈谈datahub中的血缘图。

查看源码

点击此处链接你将看到 datahub中的血缘图,
在这里插入图片描述
由于是demo环境,数据有可能会被删掉,读者可以自行寻找。

该血缘图的特性如下

  • 上下游
  • 自定义节点
  • 节点可点击,操作
  • 线的样式有多种
  • 鼠标放置线上有辅助信息
  • 可以展开上下游
  • 最基本的放大,缩小视图

F12 节点的源码,发现使用的是SVG 实现的
在这里插入图片描述
标签的类前缀都是vx,但直接搜没有搜到,于是去项目的package.json中寻找使用的库。

查看package.json

在项目中 找到了答案
https://github.com/datahub-project/datahub/blob/master/datahub-web-react/package.json
在这里插入图片描述

使用的是
https://airbnb.io/visx
在这里插入图片描述
github 地址 https://github.com/airbnb/visx
visx 是一个为 React 应用程序提供可视化功能的库。它提供了一系列低级可视化元素或组件,被称为 expressive, low-level visualization primitives,这些元素或组件可以用于创建各种可视化效果,例如饼图等。使用 VISX 可以方便地将设计元素添加到 React 应用程序中。它是由 Airbnb 构建的。

提前关键词,该库具有的特征

  • 为react
  • 低级元素
  • 可视化

低级元素是说它不直接提供一个个完整的图表,而且要使用多个元素组装实现,这也意味着 要使用它,还是有一点门槛的,但人家的审美确实在线。visx的gallery 都很美观。让人看了就像用,但一用就头大,提供的api太底层了。

大家看几个官网的示例
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

查看组件源码

上面介绍了一下 visx库,我们回到datahub这个项目
血缘图 都放在https://github.com/datahub-project/datahub/blob/master/datahub-web-react/src/app/lineage这个目录

节点组件
https://github.com/datahub-project/datahub/blob/master/datahub-web-react/src/app/lineage/LineageEntityNode.tsx

visx库文档

因为这个库并不是一个专业的Graph库,所有在图的布局算法,自定义接的,自定义线,或者图的交互 都不如g6做的丰富。
选型还需慎重,依赖大量svg的api,标签。

案例

最后提供一个 使用visx 画的一个 Graph案例
在这里插入图片描述

import React, { useState } from 'react';
import { Group } from '@visx/group';
import { hierarchy, Tree } from '@visx/hierarchy';
import { LinearGradient } from '@visx/gradient';
import { pointRadial } from 'd3-shape';
import useForceUpdate from './useForceUpdate';
import LinkControls from './LinkControls';
import getLinkComponent from './getLinkComponent';interface TreeNode {name: string;isExpanded?: boolean;children?: TreeNode[];
}const data: TreeNode = {name: 'T',children: [{name: 'A',children: [{ name: 'A1' },{ name: 'A2' },{ name: 'A3' },{name: 'C',children: [{name: 'C1',},{name: 'D',children: [{name: 'D1',},{name: 'D2',},{name: 'D3',},],},],},],},{ name: 'Z' },{name: 'B',children: [{ name: 'B1' }, { name: 'B2' }, { name: 'B3' }],},],
};const defaultMargin = { top: 30, left: 30, right: 30, bottom: 70 };export type LinkTypesProps = {width: number;height: number;margin?: { top: number; right: number; bottom: number; left: number };
};export default function Example({width: totalWidth,height: totalHeight,margin = defaultMargin,
}: LinkTypesProps) {const [layout, setLayout] = useState<string>('cartesian');const [orientation, setOrientation] = useState<string>('horizontal');const [linkType, setLinkType] = useState<string>('diagonal');const [stepPercent, setStepPercent] = useState<number>(0.5);const forceUpdate = useForceUpdate();const innerWidth = totalWidth - margin.left - margin.right;const innerHeight = totalHeight - margin.top - margin.bottom;let origin: { x: number; y: number };let sizeWidth: number;let sizeHeight: number;if (layout === 'polar') {origin = {x: innerWidth / 2,y: innerHeight / 2,};sizeWidth = 2 * Math.PI;sizeHeight = Math.min(innerWidth, innerHeight) / 2;} else {origin = { x: 0, y: 0 };if (orientation === 'vertical') {sizeWidth = innerWidth;sizeHeight = innerHeight;} else {sizeWidth = innerHeight;sizeHeight = innerWidth;}}const LinkComponent = getLinkComponent({ layout, linkType, orientation });return totalWidth < 10 ? null : (<div><LinkControlslayout={layout}orientation={orientation}linkType={linkType}stepPercent={stepPercent}setLayout={setLayout}setOrientation={setOrientation}setLinkType={setLinkType}setStepPercent={setStepPercent}/><svg width={totalWidth} height={totalHeight}><LinearGradient id="links-gradient" from="#fd9b93" to="#fe6e9e" /><rect width={totalWidth} height={totalHeight} rx={14} fill="#272b4d" /><Group top={margin.top} left={margin.left}><Treeroot={hierarchy(data, (d) => (d.isExpanded ? null : d.children))}size={[sizeWidth, sizeHeight]}separation={(a, b) => (a.parent === b.parent ? 1 : 0.5) / a.depth}>{(tree) => (<Group top={origin.y} left={origin.x}>{tree.links().map((link, i) => (<LinkComponentkey={i}data={link}percent={stepPercent}stroke="rgb(254,110,158,0.6)"strokeWidth="1"fill="none"/>))}{tree.descendants().map((node, key) => {const width = 40;const height = 20;let top: number;let left: number;if (layout === 'polar') {const [radialX, radialY] = pointRadial(node.x, node.y);top = radialY;left = radialX;} else if (orientation === 'vertical') {top = node.y;left = node.x;} else {top = node.x;left = node.y;}return (<Group top={top} left={left} key={key}>{node.depth === 0 && (<circler={12}fill="url('#links-gradient')"onClick={() => {node.data.isExpanded = !node.data.isExpanded;console.log(node);forceUpdate();}}/>)}{node.depth !== 0 && (<rectheight={height}width={width}y={-height / 2}x={-width / 2}fill="#272b4d"stroke={node.data.children ? '#03c0dc' : '#26deb0'}strokeWidth={1}strokeDasharray={node.data.children ? '0' : '2,2'}strokeOpacity={node.data.children ? 1 : 0.6}rx={node.data.children ? 0 : 10}onClick={() => {node.data.isExpanded = !node.data.isExpanded;console.log(node);forceUpdate();}}/>)}<textdy=".33em"fontSize={9}fontFamily="Arial"textAnchor="middle"style={{ pointerEvents: 'none' }}fill={node.depth === 0 ? '#71248e' : node.children ? 'white' : '#26deb0'}>{node.data.name}</text></Group>);})}</Group>)}</Tree></Group></svg></div>);
}

题外话

开源项目 openmatedata
血缘图使用的react-flow,节点,线都是使用div画的。大数据量时,可能堪忧
在这里插入图片描述

查看全文

99%的人还看了

猜你感兴趣

版权申明

本文"datahub 中血缘图的实现分析,在react中使用airbnb的visx可视化库来画有向无环图":http://eshow365.cn/6-25461-0.html 内容来自互联网,请自行判断内容的正确性。如有侵权请联系我们,立即删除!