Echarts2

563 阅读8分钟

1. Echarts图表标准配置图示技巧

2. 饼图(pie)详解标准饼图

一、基本配置demo

二、标题(title)配置

三、提示框(tooltip)配置

四、图例(legend)配置

五、系列列表(series )配置

3. 辅助线的作⽤

4. 托动⾃动计算(calculable)功能实现

1、用Echarts的type为graph类型画图,优点:拖拽不用重新定点划线,有自带的连线关系,缺点:连线的样式及特效不能很好呈现

1//注意this.viewData是从后台获取的数据
 mounted () {
2
      const that = this
3
      this.$nextTick(() => {
4
        let myChart = echarts.init(document.getElementById(this.scaleLine_id))
5
        var home = "image:///img/bussiness.png";  //业务
6
        var error_home = "image:///img/error-bussiness.png";  //故障业务
7
        var windows = "image:///img/windows.png";  //windows主机
8
        var error_windows = "image:///img/error-windows";  //故障windows
9
        var linux = "image:///img/linux.png";  //linux
10
        var error_linux = "image:///img/error-linux.png";  //故障linux
11
        var cluster0 = "image:///img/cluster_0.png";  //web服务器
12
        var error_cluster0 = "image:///img/error-cluster_0.png";  //故障web服务器
13
        var cluster1 = "image:///img/cluster_1.png";  //数据库
14
        var error_cluster1 = "image:///img/error-cluster_1.png";  //故障数据库
15
        var cluster2 = "image:///img/cluster_2.png";  //消息队列
16
        var error_cluster2 = "image:///img/error-cluster_2.png";  //故障消息队列
17
        var cluster3 = "image:///img/cluster_3.png";  //缓存
18
        var error_cluster3 = "image:///img/error-cluster_3.png";  //故障缓存
19
        var cluster4 = "image:///img/cluster_4.png";  //文件系统
20
        var error_cluster4 = "image:///img/error-cluster_4.png";  //故障文件系统
21
        let labelOne = {//文字1省级市级五区
22
          normal: {
23
            position: "bottom",
24
            distance:0, //离图标多远
25
            show: true,
26
            textStyle: {
27
              fontSize: 15,
28
              color:'#00eaff',
29
              fontFamily:'microsoftYaHei'
30
            },
31
          }
32
        };
33
        let lineColorOne = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
34
          offset: 0,
35
          color: '#58B3CC'
36
        }, {
37
          offset: 1,
38
          color: '#F58158'
39
        }])
40
        let viewData = this.viewData
41
        let data = []
42
        var symbolSize = 20;
43
        // console.log(viewData)
44
        //处理数据格式
45
        for(let i = 0;i<viewData.element.length;i++){
46
          data.push(viewData.element[i].value)
47
          if(viewData.element[i].type === 20){
48
            if(viewData.element[i].status === 1){
49
              viewData.element[i].symbol =  home
50
              viewData.element[i].symbolSize = 40
51
              viewData.element[i].itemStyle = {"normal":{"color":"#F58158"}}
52
              viewData.element[i].draggable = 'true'
53
              viewData.element[i].fixed = 'true'
54
              viewData.element[i].label = labelOne
55
            }else{
56
              viewData.element[i].symbol =  error_home
57
              viewData.element[i].symbolSize = 40
58
              viewData.element[i].itemStyle = {"normal":{"color":"#F58158"}}
59
              viewData.element[i].draggable = 'true'
60
              viewData.element[i].fixed = 'true'
61
              viewData.element[i].label = labelOne
62

63
            }
64
          }else if(viewData.element[i].type === 10){
65
            if(viewData.element[i].status === 1){
66
              viewData.element[i].symbol =  windows
67
              viewData.element[i].symbolSize = 40
68
              viewData.element[i].itemStyle = {"normal":{"color":"#F58158"}}
69
              viewData.element[i].draggable = 'false'
70
              viewData.element[i].fixed = 'true'
71
              viewData.element[i].label = labelOne
72
            }else{
73
              viewData.element[i].symbol =  error_windows
74
              viewData.element[i].symbolSize = 40
75
              viewData.element[i].itemStyle = {"normal":{"color":"#F58158"}}
76
              viewData.element[i].draggable = 'false'
77
              viewData.element[i].fixed = 'true'
78
              viewData.element[i].label = labelOne
79

80
            }
81

82
          }else if(viewData.element[i].type === 11){
83
            if(viewData.element[i].status === 1){
84
              viewData.element[i].symbol =  linux
85
              viewData.element[i].symbolSize = 40
86
              viewData.element[i].itemStyle = {"normal":{"color":"#F58158"}}
87
              viewData.element[i].draggable = 'false'
88
              viewData.element[i].fixed = 'true'
89
              viewData.element[i].label = labelOne
90
            }else{
91
              viewData.element[i].symbol =  error_linux
92
              viewData.element[i].symbolSize = 40
93
              viewData.element[i].itemStyle = {"normal":{"color":"#F58158"}}
94
              viewData.element[i].draggable = 'false'
95
              viewData.element[i].fixed = 'true'
96
              viewData.element[i].label = labelOne
97

98
            }
99

100
          }else if(viewData.element[i].type === 0){
101
            if(viewData.element[i].status === 1){
102
              viewData.element[i].symbol =  cluster0
103
              viewData.element[i].symbolSize = 40
104
              viewData.element[i].itemStyle = {"normal":{"color":"#F58158"}}
105
              viewData.element[i].draggable = 'false'
106
              viewData.element[i].fixed = 'true'
107
              viewData.element[i].label = labelOne
108
            }else{
109
              viewData.element[i].symbol =  error_cluster0
110
              viewData.element[i].symbolSize = 40
111
              viewData.element[i].itemStyle = {"normal":{"color":"#F58158"}}
112
              viewData.element[i].draggable = 'false'
113
              viewData.element[i].fixed = 'true'
114
              viewData.element[i].label = labelOne
115

116
            }
117

118
          }else if(viewData.element[i].type === 1) {
119
            if(viewData.element[i].status === 1){
120
              viewData.element[i].symbol =  cluster1
121
              viewData.element[i].symbolSize = 40
122
              viewData.element[i].itemStyle = {"normal":{"color":"#F58158"}}
123
              viewData.element[i].draggable = 'false'
124
              viewData.element[i].fixed = 'true'
125
              viewData.element[i].label = labelOne
126
            }else{
127
              viewData.element[i].symbol =  error_cluster1
128
              viewData.element[i].symbolSize = 40
129
              viewData.element[i].itemStyle = {"normal":{"color":"#F58158"}}
130
              viewData.element[i].draggable = 'false'
131
              viewData.element[i].fixed = 'true'
132
              viewData.element[i].label = labelOne
133

134
            }
135

136
          }else if(viewData.element[i].type === 2) {
137
            if(viewData.element[i].status === 1){
138
              viewData.element[i].symbol =  cluster2
139
              viewData.element[i].symbolSize = 40
140
              viewData.element[i].itemStyle = {"normal":{"color":"#F58158"}}
141
              viewData.element[i].draggable = 'false'
142
              viewData.element[i].fixed = 'true'
143
              viewData.element[i].label = labelOne
144
            }else{
145
              viewData.element[i].symbol =  error_cluster2
146
              viewData.element[i].symbolSize = 40
147
              viewData.element[i].itemStyle = {"normal":{"color":"#F58158"}}
148
              viewData.element[i].draggable = 'false'
149
              viewData.element[i].fixed = 'true'
150
              viewData.element[i].label = labelOne
151

152
            }
153

154
          }else if(viewData.element[i].type === 3) {
155
            if(viewData.element[i].status === 1){
156
              viewData.element[i].symbol =  cluster3
157
              viewData.element[i].symbolSize = 40
158
              viewData.element[i].itemStyle = {"normal":{"color":"#F58158"}}
159
              viewData.element[i].draggable = 'false'
160
              viewData.element[i].fixed = 'true'
161
              viewData.element[i].label = labelOne
162
            }else{
163
              viewData.element[i].symbol =  error_cluster3
164
              viewData.element[i].symbolSize = 40
165
              viewData.element[i].itemStyle = {"normal":{"color":"#F58158"}}
166
              viewData.element[i].draggable = 'false'
167
              viewData.element[i].fixed = 'true'
168
              viewData.element[i].label = labelOne
169

170
            }
171

172
          }else if(viewData.element[i].type === 4){
173
            if(viewData.element[i].status === 1){
174
              viewData.element[i].symbol =  cluster4
175
              viewData.element[i].symbolSize = 40
176
              viewData.element[i].itemStyle = {"normal":{"color":"#F58158"}}
177
              viewData.element[i].draggable = 'false'
178
              viewData.element[i].fixed = 'true'
179
              viewData.element[i].label = labelOne
180
            }else{
181
              viewData.element[i].symbol =  error_cluster4
182
              viewData.element[i].symbolSize = 40
183
              viewData.element[i].itemStyle = {"normal":{"color":"#F58158"}}
184
              viewData.element[i].draggable = 'false'
185
              viewData.element[i].fixed = 'true'
186
              viewData.element[i].label = labelOne
187

188
            }
189

190
          }
191
        }
192
        var linkss = viewData.line.map((item,i)=>{
193
          return {
194
            source:item.fromName.id,
195
            target:item.toName.id,
196

197
          }
198
        })
199
        console.log(linkss)
200
        const option = {
201
          grid: {
202
            left: '6%',
203
            top: '1%',
204
            bottom:'5%',
205
          },
206
          textStyle: {
207
            color: '#f00',
208
            fontSize: 16,
209
            fontWeight: 600,
210
          },
211
          // tooltip: {
212
          //   // 表示不使用默认的『显示』『隐藏』触发规则。
213
          //   triggerOn: 'none',
214
          //   formatter: function (params) {
215
          //     return 'X: ' + params.data[0].toFixed(2) + '<br>Y: ' + params.data[1].toFixed(2);
216
          //   }
217
          // },
218
          backgroundColor:"#013954",
219
         // backgroundColor:"#fff",
220
          animationDuration: 1500,
221
          animationEasingUpdate: 'quinticInOut',
222
          xAxis: {
223
            show: false,
224
            type: 'value',
225
            axisLine: {onZero: false}
226
          },
227
          yAxis: {
228
            show: false,
229
            type: 'value',
230
            axisLine: {onZero: false}
231
          },
232
          series: [
233
            {
234
            id:'a',
235
            type: 'graph',
236
            coordinateSystem: 'cartesian2d',
237
            legendHoverLink: false,
238
            hoverAnimation: true,
239
            nodeScaleRatio: false,
240
            roam: false,
241
            color: {
242
              type: 'linear',
243
              x: 0,
244
              y: 0,
245
              x2: 0,
246
              y2: 1,
247
              colorStops: [{
248
                offset: 0, color: 'red' // 0% 处的颜色
249
              }, {
250
                offset: 1, color: 'blue' // 100% 处的颜色
251
              }],
252
              global: false // 缺省为 false
253
            },
254
            edges:linkss,
255
            data: viewData.element,
256
          },
257
            ]
258
        };
259
        //折线点原本并没有拖拽功能,需要我们为它加上:使用 graphic 组件,在每个点上面,覆盖一个隐藏的可拖拽的圆点
260
        myChart.setOption(option,true);
261
        // onPointDragging(0)//初始化
262
        console.log(myChart.getOption())
263
        myChart.setOption({
264
          graphic: echarts.util.map(data, function (dataItem, dataIndex) {
265
            return {
266
              // 'circle' 表示这个 graphic element 的类型是圆点。
267
              type: 'circle',
268
              shape: {
269
                // 圆点的半径。
270
                r: symbolSize
271
              },
272
              position: myChart.convertToPixel('grid', dataItem),
273
              invisible: true,
274
              // 这个属性让圆点可以被拖拽。
275
              draggable: true,
276
              // 把 z 值设得比较大,表示这个圆点在最上方,能覆盖住已有的折线图的圆点。
277
              z: 100,
278
              ondrag: echarts.util.curry(onPointDragging, dataIndex),
279
              // 在 mouseover 的时候显示,在 mouseout 的时候隐藏。
280
              onmousemove: echarts.util.curry(showTooltip, dataIndex),
281
              onmouseout: echarts.util.curry(hideTooltip, dataIndex),
282
            };
283
          })
284
        });
285
        // 此函数中会根据拖拽后的新位置,改变 data 中的值,并用新的 data 值,重绘折线图,从而使折线图同步于被拖拽的隐藏圆点。
286
        function onPointDragging(dataIndex) {
287
          // 这里的 data 就是本文最初的代码块中声明的 data,在这里会被更新。
288
          // 这里的 this 就是被拖拽的圆点。this.position 就是圆点当前的位置。
289
          data[dataIndex] = myChart.convertFromPixel('grid', this.position);
290
          let initIndex =  that.viewData.element[dataIndex].value
291
          viewData.element[dataIndex].value = data[dataIndex]
292
          console.log( that, data[dataIndex])
293
          //元素位置改变后重新划线
294
          // for(let i = 0;i < viewData.line.length;i ++){
295
          //   if(viewData.line[i].fromName === viewData.element[dataIndex].id){
296
          //     viewData.line[i].coords[0] = data[dataIndex]
297
          //   }else if(viewData.line[i].toName === viewData.element[dataIndex].id){
298
          //     viewData.line[i].coords[1] = data[dataIndex]
299
          //   }
300
          // }
301
          // 用更新后的 data,重绘折线图。
302
          myChart.setOption(option);
303
        }
304
        function showTooltip(dataIndex) {
305
          myChart.dispatchAction({
306
            type: 'showTip',
307
            seriesIndex: 0,
308
            dataIndex: dataIndex
309
          });
310
        }
311
        function hideTooltip(dataIndex) {
312
          myChart.dispatchAction({
313
            type: 'hideTip'
314
          });
315
        }
316
        // 最后,为了使 dom 尺寸改变时,图中的元素能自适应得变化
317
        window.addEventListener('resize', function () {
318
          // 对每个拖拽圆点重新计算位置,并用 setOption 更新。
319
          myChart.setOption({
320
            graphic: echarts.util.map(data, function (item, dataIndex) {
321
              return {
322
                position: myChart.convertToPixel('grid', item)
323
              };
324
            })
325
          });
326
        });
327
        //监听点击事件
328
        myChart.on('click', function (params) {
329
          console.log(params)
330
          if(params.seriesType === "graph"){
331
            that.$emit('selectAsset',params.data.id)
332
          }
333
          //TODO
334

335
        });
336
        // //添加右件菜单
337
        // myChart.on("mousedown",function(e){
338
        //   if(e.event.event.button===2){
339
        //     //e中有当前节点信息
340
        //     showMenu(e,[
341
        //       {
342
        //         "name": "菜单1",
343
        //         "fn": function() {
344
        //           alert("触发-菜单1" + e.data);
345
        //           console.log(e);
346
        //         }
347
        //       }, {
348
        //         "name": "菜单2",
349
        //         "fn": function() {
350
        //           alert("触发-菜单2" + e.data);
351
        //         }
352
        //       }
353
        //     ]);
354
        //   }
355
        // })
356
        //
357
        // var style_ul = "padding:0px;margin:0px;border: 1px solid #ccc;background-color: #fff;position: absolute;left: 0px;top: 0px;z-index: 2;display: none;";
358
        // var style_li = "list-style:none;padding: 5px; cursor: pointer; padding: 5px 20px;margin:0px;";
359
        // var style_li_hover = style_li + "background-color: #00A0E9; color: #fff;";
360
        //
361
        // //右键菜单容器
362
        // var menubox = $("<div class='echartboxMenu' style='" + style_ul + "'><div style='text-align:center;background:#ccc'></div><ul style='margin:0px;padding:0px;'></ul></div>")
363
        //   .appendTo($(document.body));
364
        //
365
        // //移除浏览器右键菜单
366
        // myChart.getDom().oncontextmenu = menubox[0].oncontextmenu = function(){
367
        //   return false;
368
        // }
369
        //
370
        // //点击其他位置隐藏菜单
371
        // $(document).click(function() {
372
        //   menubox.hide()
373
        // });
374
        //
375
        // //显示菜单
376
        // var showMenu = function(e,menus){
377
        //   $("div", menubox).text(e.name);
378
        //   var menulistbox = $("ul", menubox).empty();
379
        //   $(menus).each(function(i, item) {
380
        //     var li = $("<li style='" + style_li + "'>" + item.name + "</li>")
381
        //       .mouseenter(function() {
382
        //         $(this).attr("style", style_li_hover);
383
        //       })
384
        //       .mouseleave(function() {
385
        //         $(this).attr("style", style_li);
386
        //       })
387
        //       .click(function() {
388
        //         item["fn"].call(this);
389
        //         menubox.hide();
390
        //       });
391
        //     menulistbox.append(li);
392
        //   });
393
        //   menubox.css({
394
        //     "left": event.x,
395
        //     "top": event.y
396
        //   }).show();
397
        // }
398


        /*窗口自适应,关键代码*/

        setTimeout(function (){

          window.onresize = function () {

            myChart.resize();

          }

        },200)

        // window.addEventListener('resize', function () {

        //     myChart.resize();

        // });

      })
    },


复制代码

5. 饼图(pie)⾼级 环形图

img

6. 饼图(pie)⾼级嵌套环形图

7. 散点图(scatter)⾼级 提示信息及坐标轴的⾃定义

8. ⽓泡图(bubble)详解

echarts本身是有气泡图的,官方气泡图的特点是每个气泡的位置是基于坐标轴进行定位,如图1和2所示。但是本文所介绍的气泡图并不是官方所介绍的气泡图,而是一类区别于官方的图表类型,这种图表类型通常采用d3.js插件实现,如图3所示。从图1、图2、图3可以看出两种气泡图的展示,各有其特点。图1和图2,虽然是基于坐标轴,但气泡图之间容易叠加在一起。图3所示的气泡图则没有这个缺点,气泡之间互相相邻,彼此之间各不侵犯。众多周知,d3.js主要是svg技术实现的图表插件,而Echarts则主要是基于canvas画布技术实现的。两者之间虽然互相没有关联,但实现的算法确是可以参考和借鉴的。

img

实现思路:

通过研究d3.js气泡图的代码构建过程,我发现其实现的核心主要在于计算每一个气泡的x、y、r,这三个跟气泡生成相关的属性值,只要把这三个属性值求出来了,那么问题自然就可以解决了。然后采用Echarts插件配置项中的graphic接口,添加circle和text类型元素,就可以渲染成一个个气泡。

具体实现:

(1)求解每一个气泡的x、y、r属性。这个工作很麻烦,涉及的算法,也挺复杂的。通过研究d3.js,我发现其求解每一个气泡的属性代码就有500多行。主要是思路是,首先构建一个所有气泡的根气泡(这个根气泡会包含所有的子气泡,但不会在页面上渲染出来,它主要作用是定位和布局),接着求解每一个气泡的x、y、r的值,特别指出的是每一个气泡的半径r,是通过其值得开根号求出的,最后根据根气泡的信息和渲染区域大小再判断是否要缩放气泡(即缩放x、y、半径r)。具体的求解代码如下所示,其中有两个重要的函数echarts.util.pack和echarts.util.hierarchy,这两个接口是求解气泡属性的关键,具体的实现源码,我已从d3.js插件里面将其独立出来了。

    /**
       * 获取气泡图的根气泡和各气泡的属性:颜色、坐标位置(x,y)、半径(r)
       * @param {Function}  data: 初始化数据
       * @param {Function}  pack: 布局函数
       * @return {Array} 气泡信息集合            
    */
    function processBubbleData(data, container) {
        var width = container.style.width.replace('px', '');
        var height = container.style.height.replace('px', '');
        //初始化气泡图显示的布局(主要是宽度和高度) 
        var pack = echarts.util.pack()
        .size([width, height])
        .padding(1);

        var index = 0;              
        var color = ["#1f77b4", "#aec7e8", "#ff7f0e", "#ffbb78", "#2ca02c", "#98df8a", "#d62728", "#ff9896", "#9467bd", "#c5b0d5",          
        "#8c564b", "#c49c94", "#e377c2", "#f7b6d2", "#7f7f7f", "#c7c7c7", "#bcbd22", "#dbdb8d", "#17becf", "#9edae5"];  

        var root = echarts.util.hierarchy({children: data})
                   .sum(function(d) { return d.value; })
                   .each(function(d){
                       d.color = color[index % 20];
                       index++;
                       return d.color;
                   });

        return pack(root).leaves(); 
    }
复制代码

(2)使用Echarts插件渲染气泡图。 众所周知,Echarts插件的配置主要是option的配置,而气泡图的配置主要是option中graphic接口的配置。而本文在设计中将一个气泡和其相关的文本看成一个集合。如下所示是具体实现源码。

    /**
       * 绘制Echarts图表
       * @param {Object} container: dom实例
    */
    function drawChart(chart, bubbleData) {                                                         
        var option = {
             tooltip: {
                show: true,
                triggerOn: 'none',
                formatter: function (params) {
                    return '<span  style="border-radius: 6px;width: 12px;height: 12px;display: inline-block;background-color:' + params.color +'"></span>' + ' ' +  params.data.name + ':  ' + params.data.value;
                }
            },
            graphic: getCircleOption(bubbleData, chart),
            series: []
        };          
        chart.setOption(option);
    }

    /**
       * 获取气泡的graphic配置项
       * @param {Array} processData: 气泡的属性信息集合
       * @param {Object} chart: echarts实例
       * @return {Array}  配置项集合
    */          
    function getCircleOption(processData, chart) {
         return echarts.util.map(processData, function (item, dataIndex) {
            return {
                type: 'group',
                id: 'circleGroup' + dataIndex,
                bounding: 'raw',
                color: item.color,
                data: item.data,    
                children: getChildOption(item, dataIndex, chart),                           
                onmousemove: function (event) {
                    showTooltip.call(this, event, chart);                                                   
                },
                onmouseout: function (event) {                       
                    hideTooltip.call(this, event, chart);
                }
            };
        });                             
    }
    /**
       * 得到各个气泡和文本节点配置信息
       * @param {Object} item: 气泡的属性信息
       * @dataIndex {Number} dataIndex: 气泡的序号
       * @param {Object} chart: echarts实例
       * @return {Array} children: 配置项集合
    */
    function getChildOption(item, dataIndex, chart) {
        var children = [];
        children.push({
            id: 'circle' + dataIndex,
            type: 'circle',
            shape: {
                cx: item.x,
                cy: item.y,
                r: item.r 
            },
            name: 'mainElem',
            style: {
                fill: item.color
            },
            eventData: {
                id: 'circle' + dataIndex
            },
            onclick: bindClickEvent         
        });
        children.push({                   
            type: 'text',                   
            style: {
                fill: '#fff',
                text: item.data.name,
                x: item.x - item.r / 4,
                y: item.y
            },
            value: item.value
        });
        return children;
        /**
           * 气泡绑定click事件
        */
        function bindClickEvent() {
            var circle = new echarts.graphic.Circle({
               shape: this.shape,
               style: {
                   stroke: 'green',
                   fill: null,
                   lineWidth: 2     
               }
            });
            this.parent.add(circle);
            chart.addClickElem(circle);
        }
    }
复制代码

(3)配置每一个气泡的提示框 Echarts是有提示框组件的,叫tooltip,显示和隐藏的时候调用的接口都是dispatchAction,只是传递的参数不一样而已。

**显示提示框。**
有下面两种使用方式。
1 指定在相对容器的位置处显示提示框,如果指定的位置无法显示则无效。
dispatchAction({
    type: 'showTip',
    // 屏幕上的 x 坐标
    x: number,
    // 屏幕上的 y 坐标
    y: number,
    // 本次显示 tooltip 的位置。只在本次 action 中生效。
    // 缺省则使用 option 中定义的 tooltip 位置。
    position: Array.<number>|string|Function
})

2 指定数据图形,根据 tooltip 的配置项显示提示框。

dispatchAction({
    type: 'showTip',
    // 系列的 index,在 tooltip 的 trigger 为 axis 的时候可选。
    seriesIndex?: number,
    // 数据的 index,如果不指定也可以通过 name 属性根据名称指定数据
    dataIndex?: number,
    // 可选,数据名称,在有 dataIndex 的时候忽略
    name?: string,
    // 本次显示 tooltip 的位置。只在本次 action 中生效。
    // 缺省则使用 option 中定义的 tooltip 位置。
    position: Array.<number>|string|Function,
})

**隐藏提示框。**
dispatchAction({
    type: 'hideTip'
})
复制代码

针对本文的情况,主要采用的是显示的第一个接口,由于本文没有采用配置项中的series属性,故采用显示框的第一种方法,如下代码所示。特别注意的是formatterParams参数的配置,一定要有,不然就无法显示出提示框。

    /**
       * 利用dispatchAction函数显示每个气泡信息
       *@param {Object} event 
       *@param {Object} chart: echarts instance
    */
    function showTooltip(event, chart) {
        chart.dispatchAction({
            type: 'showTip',
            // 屏幕上的 x 坐标
            x: event.offsetX,                   
            // 屏幕上的 y 坐标
            y: event.offsetY,
            tooltip: {
                formatterParams: {
                    color: this.color,
                    data: this.data
                }                       
            }                   
        }); 
        //显示气泡的边界条纹             
        this.childOfName('mainElem').setStyle({
             stroke: 'green',
             lineWidth: 2                                
        });                 
    }
    /**
      * 隐藏提示框
      *@param {Object} event 
      *@param {Object} chart: echarts instance
    */
    function hideTooltip(event, chart) {
        chart.dispatchAction({
            type: 'hideTip'
        });             
        this.childOfName('mainElem').setStyle({
             stroke: null                        
        });
    }
复制代码

(4)绑定有利于用户操作的事件,如点击气泡图,气泡边缘变绿色等。

    /**
      * 图表添加事件处理函数
      * @param {Object} chart: echarts实例
    */
    function bindChartEvent(chart) {
        //点击气泡后的回调
        chart.addClickElem = function (elem) {
            if (this.currentElem !== elem && this.currentElem != null) {
                this.currentElem.parent.remove(this.currentElem);           
            }
            this.currentElem = elem;
        };  

        //点击canvas中非气泡部分                
        chart._zr.on('click', function (params) {
            if(params.target == null) {
                if (chart.currentElem != null) {
                    chart.currentElem.parent.remove(chart.currentElem); 
                    chart.currentElem = null;                       
                }
            }
        });

        //点击页面中非canvas部分
        document.onclick = function (event) {
            if (event.toElement.tagName != 'CANVAS') {
                if (chart.currentElem != null) {
                    chart.currentElem.parent.remove(chart.currentElem); 
                    chart.currentElem = null;                       
                }
            }
        };
    }
复制代码

总结: 以上就是气泡图实现的主要代码,难点主要是气泡属性信息的计算,这一块主要采用的是d3.js的算法,本人主要是作为一个代码的搬运工,将其从d3.js中独立出来,并嵌入到Echarts中。

        require(['bubbleUtil'],function(bubbleUtil){
            var data = [ 
                {"name":"黄瓜", "value":12345},
                {"name":"猕猴桃", "value":7345},
                {"name":"红提", "value":1345},
                {"name":"枣子", "value":2345}, 
                {"name":"火龙果", "value":345}, 
                {"name":"葡萄", "value":14534},
                {"name":"橘子", "value":9435} ,
                {"name":"柚子", "value":134} ,
                {"name":"桃子", "value":6346} ,
                {"name":"西红柿", "value":634} ,
                {"name":"梨子", "value":6346} ,
                {"name":"苹果", "value":3644} ,
                {"name":"香蕉", "value":2346} ,
                {"name":"小明", "value":3869} ,
                {"name":"小米", "value":6534} ,
                {"name":"大米", "value":45} ,
                {"name":"玉米", "value":8345}             
            ];  

            bubbleUtil(data, document.getElementById('container'));         
        });