开发需求背景

今天领导派了一个小活,要求我将公司的物联网平台的网络拓扑图画出来。做一个数据展示的页面,集成到现有的iot平台上。

说到拓扑图,大家都也都比较清楚,能够清晰地表示网络链路的链接关系。

官方一点的解释是:
网络拓扑结构是指用传输媒体互连各种设备的物理布局(将参与LAN工作的各种设备用媒体互连在一起有多种方法,但是实际上只有几种方式能适合LAN的工作)。
网络拓扑图是指由网络节点设备和通信介质构成的网络结构图。

一般的拓扑图都是这样子的

python echarts 拓扑图 echarts画拓扑图_数据

还有这一种

python echarts 拓扑图 echarts画拓扑图_动效_02

这些设计图都是架构师使用软件画出来的,数据都是固定的,不支持动态修改,没有动效,而我们需要支持动态添加网络节点。需要有动效。

经过一天不歇的努力,终于在下班前做出来了一个原型图。

python echarts 拓扑图 echarts画拓扑图_动效_03


先给大家看看,下面来详细讲解如何使用echarts一步一步完成这个拓扑图的。

技术调研

接到需求后我调研了几个物联网仪表盘后,我觉得使用echart来试试,因为d3太难了。😃

拓扑图中需要与几个实体,代表链路中的每一个设备,一个设备可能有进线,有出线,也有可能有多条线,线的条数都不能固定的。因为随着设备的增加,中央机器到达各个设备的线也是逐渐增加的。设备之间有线,有了线,就要有流量的流动,就是在线上方覆盖一个箭头,逐渐从数据的发出方到达数据的接受方。此外每台设备都要有个名称,鼠标放到设备上,需要有一个动效,最好能显示一些详细情况。

ok
需求分析完毕,一个图大致有以下几个视图

  • 设备
  • 设备之间的线
  • 线上的流量流向箭头
  • 设备的附加信息
  • 其他动效

开始编码

在真正的编码开始之前,我们需要对这个需求有个抽象的概念,怎么抽象那?
我们可以将设备,线,箭头,都看做物品,而这些物品都存在于一个平面直角坐标系上。有了平面直角坐标系也就有了他们各自的位置。
比如设备1的坐标是x: 500 y: 1000, 设备2的坐标是 x: 500 y: 600

那么设备2就是在设备1的下方。 而设备1到设备2的链接,也就是好说了, 这条线段的的气质坐标是{x: 500 y: 1000} => {x: 500, y: 600} 这样就非常容易理解了。而echarts是支持 使用二维的直角坐标系(也称笛卡尔坐标系)。
具体设置是在series 中的一些类型下, 设置coordinateSystemcartesian2d

官方文档的解释。

python echarts 拓扑图 echarts画拓扑图_数据_04

思路清晰了,我们先来做一个简单的。
先画出二个设备,然后再连线。

定义二个设备

{
  x: 500,
  y: 1000,
  nodeName: '服务器',
  svgPath: 'M544 552.325V800a32 32 0 0 1-32 32 31.375 31.375 0 0 1-32-32V552.325L256 423.037a32 32 0 0 1-11.525-43.512A31.363 31.363 0 0 1 288 368l224 128 222.075-128a31.363 31.363 0 0 1 43.525 11.525 31.988 31.988 0 0 1-11.525 43.513L544 551.038z m0 0,M64 256v512l448 256 448-256V256L512 0z m832 480L512 960 128 736V288L512 64l384 224z m0 0',
  symbolSize: 70,
},
{
  x: 500,
  y: 600,
  nodeName: '设备1',
  svgPath: 'M1172.985723 682.049233l-97.748643-35.516964a32.583215 32.583215 0 0 0-21.830134 61.582735l25.7398 9.123221-488.744218 238.181638L115.670112 741.349163l47.245961-19.223356a32.583215 32.583215 0 0 0-22.808051-60.604819l-119.579777 47.896905a32.583215 32.583215 0 0 0 0 59.952875l557.820313 251.540496a32.583215 32.583215 0 0 0 27.695632 0l570.527227-278.584184a32.583215 32.583215 0 0 0-3.258721-59.952875z,M1185.041693 482.966252l-191.587622-68.749123a32.583215 32.583215 0 1 0-21.831133 61.254764l118.927833 43.010323-488.744218 237.855666-471.474695-213.744727 116.973-47.244961a32.583215 32.583215 0 1 0-24.111938-60.604819l-190.609705 75.593537a32.583215 32.583215 0 0 0-20.528246 29.650465 32.583215 32.583215 0 0 0 20.528246 30.30141l557.819313 251.866468a32.583215 32.583215 0 0 0 27.695632 0l570.201254-278.584184a32.583215 32.583215 0 0 0 18.24744-30.953354 32.583215 32.583215 0 0 0-21.505161-29.651465z,M32.583215 290.075742l557.819313 251.540496a32.583215 32.583215 0 0 0 27.695632 0l570.201254-278.584184a32.583215 32.583215 0 0 0-3.257721-59.952875L626.244463 2.042365a32.583215 32.583215 0 0 0-23.134022 0l-570.527226 228.080502a32.583215 32.583215 0 0 0-19.224357 30.627382 32.583215 32.583215 0 0 0 19.224357 29.325493zM615.817355 67.534767l474.733416 170.408432-488.744218 238.180638-471.474695-215.372588z'
},

x, y 代表设备的坐标, nodeName定义了设备的名称, svgPath是使用svg的指令语法来生成一个设备图标。
效果图

python echarts 拓扑图 echarts画拓扑图_python echarts 拓扑图_05

定义连线

{
            coords:
            [
                [500, 1000],
                [500, 800],
            ]
        },
		{
            coords: [
                [500, 800],
                [500, 600],

            ]
        },

效果图

python echarts 拓扑图 echarts画拓扑图_数据_06


解释:

coords作为一个数组,存放的是几个点的坐标

[
   [500, 1000],
   [500, 800],
]

数组的第一个元素表示起点,第二个元素表示终点
那么这条线段就是从 x500,y1000 到达 x500,y800

完整的代码

var nodes = [{
        x: 500,
        y: 1000,
        nodeName: '应用',
        svgPath: 'M544  ',
        symbolSize: 70,

    }, {
        x: 100,
        y: 600,
        nodeName: '模块1',
        svgPath: 'M1172.  '
    },
    {
        x: 500,
        y: 600,
        nodeName: '模块2',
        svgPath: 'M1 615.817355 67.534767l474.733416 170.408432-488.744218 238.180638-471.474695-215.372588z'
    },
    {
        x: 900,
        y: 600,
        nodeName: '模块3',
        svgPath: 'M117 615.817355 67.534767l474.733416 170.408432-488.744218 238.180638-471.474695-215.372588z'
    },
    {
        x: 0,
        y: 300,
        nodeName: '节点1',
        svgPath: 'M887.5  68 68.25472-68.224 68.28544l-0.06144-0.06656z',
    },
    {
        x: 300,
        y: 300,
        nodeName: '节点2',
        svgPath: 'M88  37.71392 0 68.29056 30.57152 68.28544 68.29056 0 37.68832-30.53568 68.25472-68.224 68.28544l-0.06144-0.06656z',
    },
    {
        x: 700,
        y: 300,
        nodeName: '节点3',
        svgPath: 'M887.55  25472-68.224 68.28544l-0.06144-0.06656z',
    },
    {
        x: 1000,
        y: 300,
        nodeName: '节点4',
        svgPath: 'M887.55 0.53568 68.25472-68.224 68.28544l-0.06144-0.06656z',
    },
]
var charts = {
    nodes: [],
    linesData: [{
            coords: [
                [500, 1000],
                [500, 800],
            ]
        }, {
            coords: [
                [500, 800],
                [100, 800],
                [100, 600]

            ]
        }, {
            coords: [
                [500, 800],
                [500, 600],

            ]
        }, {
            coords: [
                [500, 800],
                [900, 800],
                [900, 600]

            ]
        },
        {
            coords: [
                [100, 600],
                [0, 300]
            ]
        },
        {
            coords: [
                [100, 600],
                [300, 300]
            ]
        },
        {
            coords: [
                [900, 600],
                [700, 300]
            ]
        },
        {
            coords: [
                [900, 600],
                [1000, 300]
            ]
        }
    ]
}
for (var j = 0; j < nodes.length; j++) {
    const {
        x,
        y,
        nodeName,
        svgPath,
        symbolSize
    } = nodes[j];
    var node = {
        nodeName,
        value: [x, y],
        symbolSize: symbolSize || 50,
        symbol: 'path://' + svgPath,
        itemStyle: {
            color: 'orange',
        }
    }
    charts.nodes.push(node)
}

option = {
    backgroundColor: "#0B1321",
    xAxis: {
        min: 0,
        max: 1000,
        show: false,
        type: 'value'
    },
    yAxis: {
        min: 0,
        max: 1000,
        show: false,
        type: 'value'
    },
    series: [{
        type: 'graph',
        coordinateSystem: 'cartesian2d',
        label: {
            show: true,
            position: 'bottom',
            color: 'orange',
            formatter: function(item) {
                return item.data.nodeName
            }
        },
        data: charts.nodes,
    }, {
        type: 'lines',
        polyline: true,
        coordinateSystem: 'cartesian2d',
        lineStyle: {
            type: 'dashed',
            width: 2,
            color: '#175064',
            curveness: 0.3

        },
        effect: {
            show: true,
            trailLength: 0.1,
            symbol: 'arrow',
            color: 'orange',
            symbolSize: 8
        },
        data: charts.linesData
    }]
};

这里svg的指令太多,我删减了一部分,具体的动效图。

python echarts 拓扑图 echarts画拓扑图_动效_07

技术回顾

使用echart画物联网各种仪表盘时,掌握坐标系就掌握了各个物体的位置。随你发挥.
此外需要注意一下,我们的设备图表使用的是series-graph 可以用于展现节点以及节点之间的关系数据。

写在最后

掌握住echarts的配置项,可以画出很多精美的仪表盘,后续我和大家继续分享其他的物联网相关的仪表盘.如果文章对你有帮助,请记得点赞留言。