不重叠且分布均匀的气泡图解决方案(基于echarts)

气泡图是数据可视化需求中的一种常见图表形式。然而截至目前,像echarts,d3,higncharts都还没有直接提供一个配置来完成不重叠且分布均匀的气泡图这项工作。幸运的是,我们可以通过配置echart的关系图来完成我们的气泡图需求。

下面是完成后的气泡图效果(数据加载后有一个动画效果,而且气泡支持拖动,下方动图不和谐的地方为鼠标拖动):

最终稳定之后的效果如下:

为了有更好的适用性,这里把绘制气泡图的操作都封装到了一个函数里面。以Vue项目为例,要完成这个气泡图的配置所需步骤如下:

1:npm下载echarts模块。

2:在main.js文件中引入关系图相关的模块,并把echart主模块绑定到vue原型对象上,以便后续调用,代码如下:

1
2
3
4
5
6
// 引入 EChart 主模块
let echarts = require('echarts/lib/echarts')
// 引入图表
require('echarts/lib/chart/graph');

Vue.prototype.$chart = echarts

3: 编写气泡图的配置函数,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
    // 入参说明:
    // 1. data 原始气泡数据,是一个对象数组,形如[{name: '可乐', amount: 49}]
    // 2. format 数组依次指定气泡中展示的名称以及影响气泡大小的数据值, 形如['name', 'amount']
    // 3. dom 气泡图绘制所需要的dom id名
    initBubbleChart (data = [], format = [], dom) {
      let [maxValue, temp] = [0, []]
      data.forEach(item => {
        temp.push(item[format[1]])
      })
      maxValue = Math.max.apply(null, temp)

      // 气泡颜色数组
      let color = [
        '#FFB600', '#886CFF', '#0084FF',
        '#4CB690','#58B458', '#6C6C6C',
        '#F56161', '#FC754C','#5F5EEC'
      ]
      // 气泡颜色备份
      let bakeColor = [...color]
      // 气泡数据
      let bubbleData  = []
      // 气泡基础大小
      let basicSize = 70
      // 节点之间的斥力因子,值越大,气泡间距越大
      let repulsion = 380
      // 根据气泡数量配置基础大小和斥力因子(以实际情况进行适当调整,使气泡合理分布)
      if (data.length >= 5 && data.length < 10) {
        basicSize = 50
        repulsion = 230
      }
      if (data.length >= 10 && data.length < 20) {
        basicSize = 40
        repulsion = 150
      } else if (data.length >= 20) {
        basicSize = 30
        repulsion = 75
      }

      // 填充气泡数据数组bubbleData
      for (let item of data) {
        // 确保气泡数据条数少于或等于气泡颜色数组大小时,气泡颜色不重复
        if (!bakeColor.length) bakeColor = [...color]
        let colorSet = new Set(bakeColor)
        let curIndex = Math.round(Math.random()*(colorSet.size - 1))
        let curColor = bakeColor[curIndex]
        colorSet.delete(curColor)
        bakeColor = [...colorSet]
        // 气泡大小设置
        let size = (item[format[1]] * basicSize * 2) / maxValue
        if (size < basicSize) size = basicSize

        bubbleData.push({
          "name": item[format[0]],
          "value": item[format[1]],
          "symbolSize": size,
          "draggable": true,
          "itemStyle": {
              "normal": {"color": curColor}
          }
        })
      }

      let bubbleId = document.getElementById(dom)
      let bubbleChart = this.$chart.init(bubbleId)
      let bubbleOptions = {
          backgroundColor: '#fff',
          animationEasingUpdate: 'bounceIn',
          series: [{
              type: 'graph',
              layout: 'force',
              force: {
                  repulsion: repulsion,
                  edgeLength: 10
              },
              // 是否开启鼠标缩放和平移漫游
              roam: false,
              label: {normal: {show: true}},
              data: bubbleData
          }]
      }
      bubbleChart.setOption(bubbleOptions)
    }

4: 在模板中加一个标签用于提供气泡图所需的dom,如下所示:

1
<div id="bubble" style="width: 500px;height: 380px;border: 1px solid #dadada"></div>

5: 准备测试数据,并调用initBubbleChart 方法,如下所示:

1
2
3
4
5
6
7
let data = [
  {label: '可乐', amount: 100},
  {label: '雪碧', amount: 70},
  {label: '土豆', amount: 30},
  {label: '饼干', amount: 50}
]
this.initBubbleChart(data, ['label', 'amount'], 'bubble')

完成之后就可以看到一开始的效果了。这里绘制气泡图的方法,可以根据指定的气泡数据中的某个值去相对的设置气泡大小,同时还支持自定义气泡的颜色。(这里不建议通过一个随机函数去获取气泡颜色,因为这样随机的颜色组合起来大都是无法搭配的,所以这里建议自己挑颜色,或者让UI给你8,9种颜色,这样整个气泡图才会有一个比较好的展示效果)。

下面我们看一下,不同气泡数据量下的一个展示效果图(气泡基础大小,间距,还有颜色可以自己去定义):

随着气泡数量的增多,上面的整体展示效果还是可以的。同时也可以看到,气泡的数据量不宜过多,否则会显得比较拥挤。有关echarts关系图的更多配置可以移步echarts官网。本次不重叠且分布均匀的气泡图解决方案介绍就到此结束了,有任何疑问可与博客下方留言。