echarts实现自动显示tooltip

最近需要实现echarts图形中hover效果轮播(即tooltip在各个数据点上轮流显示)的功能,以下就是我学习的一个过程,只是提供思路,具体场景需要自己修改。(仅针对echarts 2.2.7及以下版本,最后的代码有3.0以上的使用方法)

 

关于echarts大家可以查看官网文档

文档中有这么一段话:

自2.1.8起,我们为echarts开发了专门的合并压缩工具echarts-optimizer。如你所发现的,build文件夹下已经包含了由echarts-optimizer生成的单文件:

  • dist(文件夹) : 经过合并、压缩的单文件
    • echarts.js : 这是包含AMD加载器的echarts主文件,需要通过script最先引入
    • chart(文件夹) : echarts-optimizer通过依赖关系分析同时去除与echarts.js的重复模块后为echarts的每一个图表类型单独打包生成一个独立文件,根据应用需求可实现图表类型按需加载
      • line.js : 折线图(如需折柱动态类型切换,require时还需要echarts/chart/bar)
      • bar.js : 柱形图(如需折柱动态类型切换,require时还需要echarts/chart/line)
      • scatter.js : 散点图
      • k.js : K线图
      • pie.js : 饼图(如需饼漏斗图动态类型切换,require时还需要echarts/chart/funnel)
      • radar.js : 雷达图
      • map.js : 地图
      • force.js : 力导向布局图(如需力导和弦动态类型切换,require时还需要echarts/chart/chord)
      • chord.js : 和弦图(如需力导和弦动态类型切换,require时还需要echarts/chart/force)
      • funnel.js : 漏斗图(如需饼漏斗图动态类型切换,require时还需要echarts/chart/pie)
      • gauge.js : 仪表盘
      • eventRiver.js : 事件河流图
      • treemap.js : 矩阵树图
      • venn.js : 韦恩图
  • source(文件夹) : 经过合并,但并没有压缩的单文件,内容同dist,可用于调试

要的就是source文件下面的文件,可以调试,把source下面的echarts-all.js导入自己的工程,在找一个例子就可以运行看效果了。

 1 <div id="chart" style="width: 800px; height: 500px;">
 2 </div>
 3 <span id="hover-console"></span>
 4 <span id="console"></span>
 5 
 6 <script src="./js/echarts-all.js"></script>
 7 <script type="text/javascript">
 8     // 基于准备好的dom,初始化echarts图表
 9     var myChart = echarts.init(document.getElementById('chart'));
10     console.log(myChart);
11     var option = {
12         tooltip: {
13             show: true
14         },
15         legend: {
16             data:['销量']
17         },
18         xAxis : [
19             {
20                 type : 'category',
21                 data : ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
22             }
23         ],
24         yAxis : [
25             {
26                 type : 'value'
27             }
28         ],
29         series : [
30             {
31                 "name":"销量",
32                 "type":"bar",
33                 "data":[5, 20, 40, 10, 10, 20]
34             }
35         ]
36     };
37 
38     // 为echarts对象加载数据
39     myChart.setOption(option);
40 
41     var ecConfig = echarts.config;
42     function eConsole(param) {
43         var mes = '' + param.type + '';
44         if (typeof param.seriesIndex != 'undefined') {
45             mes += '  seriesIndex : ' + param.seriesIndex;
46             mes += '  dataIndex : ' + param.dataIndex;
47         }
48         if (param.type == 'hover') {
49             document.getElementById('hover-console').innerHTML = 'Event Console : ' + mes;
50         }
51         else {
52             document.getElementById('console').innerHTML = mes;
53         }
54     }
55 
56     function eHover(param) {
57         var mes = '' + param.type + '';
58         if (typeof param.seriesIndex != 'undefined') {
59             mes += '  seriesIndex : ' + param.seriesIndex;
60             mes += '  dataIndex : ' + param.dataIndex;
61         }
62         document.getElementById('hover-console').innerHTML = 'Event Console : ' + mes;
63     }
64     myChart.on(ecConfig.EVENT.HOVER, eHover);
65 </script>

先说一下大概思路,由于是canvas上面绘图,所以界面上没有对应的dom元素,所以没法用js中的事件来控制。

我们要触发事件,就需要先得到图上面的数据元素,然后再考虑怎么触发事件。 

 

稍微看一下源码,就发现里面经常出现zrender,所以要先弄清楚zrender做什么的,查看zrender资料

看了zrender的介绍,大概知道是用来处理canvas的绘画的,同时还封装了dom的事件(模拟)。

了解了zrender之后还是继续调试看源码,echarts-all.js文件太大,不太方便,可以下载zrender的源码查看对应的代码文件。

 

最开始我是想从例子中的hover事件入手,进行调试查看,最后查看到实际是mousemove事件触发的,然后发现zrender中有个storage。

查看了一下storage:

 1     /**
 2      * 内容仓库 (M)
 3      * @alias module:zrender/Storage
 4      * @constructor
 5      */
 6     var Storage = function () {
 7         // 所有常规形状,id索引的map
 8         this._elements = {};
 9 
10         this._roots = [];
11 
12         this._displayList = [];
13 
14         this._displayListLen = 0;
15     };

 是不是发现了新大陆!!!_elements,对,我们要的就是它。

但是怎么得到呢,后面在echarts中找到了getZrender(),添加代码:

1         var zrender = myChart.getZrender();
2         var elements = zrender.storage._elements;
3         console.log(elements);

运行后可以在console中看见elements的内容:

确实是我们想要的。

 然后就是要处理触发事件了,怎么在指定坐标触发事件呢?网上查了查没查到相关信息,很多网友说的是不能再指定坐标触发事件,当时我就懵逼了!!!

但是想想,zrender里面封装了事件的,可以看看怎么从这里入手,是的,最后找到了解决办法:

1                         zrender.trigger('mousemove', {
2                             zrenderX: style.x,
3                             zrenderY: style.y
4                         });

试了下,成功了!!!!!!

 

说的比较粗糙,此文仅供参考,如过您有更好的方法希望能够分享出来,大家一起学习,哈哈!

下面贴上完整代码:

  1 <!DOCTYPE html>
  2 <html lang="en">
  3 <head>
  4     <meta charset="UTF-8">
  5     <title>Title</title>
  6     <style>
  7         .chart {
  8             height: 500px;
  9             width: 800px;
 10         }
 11     </style>
 12 </head>
 13 <body>
 14 <div id="chart" class="chart">
 15 
 16 </div>
 17 <span id="hover-console"></span>
 18 <span id="console"></span>
 19 
 20 <script src="./js/echarts-all.js"></script>
 21 <script type="text/javascript">
 22     // 基于准备好的dom,初始化echarts图表
 23     var myChart = echarts.init(document.getElementById('chart'));
 24     console.log(myChart);
 25     var option = {
 26         tooltip: {
 27             show: true
 28         },
 29         legend: {
 30             data:['销量']
 31         },
 32         xAxis : [
 33             {
 34                 type : 'category',
 35                 data : ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
 36             }
 37         ],
 38         yAxis : [
 39             {
 40                 type : 'value'
 41             }
 42         ],
 43         series : [
 44             {
 45                 "name":"销量",
 46                 "type":"bar",
 47                 "data":[5, 20, 40, 10, 10, 20]
 48             }
 49         ]
 50     };
 51 
 52     // 为echarts对象加载数据
 53     myChart.setOption(option);
 54 
 55     var ecConfig = echarts.config;
 56     function eHover(param) {
 57         var mes = '' + param.type + '';
 58         if (typeof param.seriesIndex != 'undefined') {
 59             mes += '  seriesIndex : ' + param.seriesIndex;
 60             mes += '  dataIndex : ' + param.dataIndex;
 61         }
 62         document.getElementById('hover-console').innerHTML = 'Event Console : ' + mes;
 63     }
 64     myChart.on(ecConfig.EVENT.HOVER, eHover);
 65 
 66     //可以获取有效的数据元素,数据元素属性包含坐标点和长宽(如果页面有变化需要重新获取)
 67     var counts = option.series[0].data.length;
 68 
 69     setTimeout(function() {
 70         autoHover();
 71         setInterval(autoHover, 1000 * counts);
 72     }, 1000);
 73 
 74 
 75     function autoHover() {
 76         var zrender = myChart.getZrender();
 77         var elements = zrender.storage._elements;
 78         var times = 0;
 79         console.log(elements);
 80 
 81         for (var key in elements) {
 82             var style = elements[key].style;
 83 
 84             //根据series中的一系列name值对elements进行归类排序,然后在进行hover
 85             //过滤条件需要完善
 86             if (elements[key]._echartsData) {
 87                 console.log(style);
 88                 (function (style, times) {
 89                     setTimeout(function () {
 90                         zrender.trigger('mousemove', {
 91                             zrenderX: Math.ceil(style.x + style.width/2),
 92                             zrenderY: Math.ceil(style.y + style.height/2)
 93                         });
 94                     }, 1000 * times);
 95                 })(style, times);
 96 
 97                 times++;
 98                 times %= counts;
 99             }
100         }
101     }
102 </script>
103 </body>
104 </html>

当鼠标触发hover时要取消自动效果,这个就大家自己解决了!哈哈

第一次发文,见笑了!

 

PS:这个问题发现上面的处理不合适哈,echarts中提供了tooltip显示的方法,一直没更新,今天得空更新下。

 

为了解决有鼠标触发hover时怎么控制轮播效果的停止和开始,更细的去查看echarts和zrender的事件,看来看去感觉外部处理很困难,最后思路还是回到了tooltip上。
最后发现tooltip中其实是有提供showTip和hideTip方法,然后看echarts文档上component中的tooltip也有写这两个方法,然后添加代码:

1 var tooltip = myChart.component.tooltip;
2 //showTip方法参数请参见echarts文档
3 tooltip.showTip({seriesIndex: '1', dataIndex: '1'}); 

运行发现根本没效果(bar类型)。。。。。。。。。。。。。调试发现tooltip中showTip()源码:

 1  if (isAxisTrigger) {
 2      var dataIndex = params.dataIndex;
 3      switch (chart.type) {
 4          case ecConfig.CHART_TYPE_LINE:
 5          case ecConfig.CHART_TYPE_BAR:
 6          case ecConfig.CHART_TYPE_K:
 7          case ecConfig.CHART_TYPE_RADAR:
 8          //问题就在这儿,serie.data[0].value是什么鬼? 
 9             if (this.component.polar == null || serie.data[0].value.length <= dataIndex) {
10                 return;
11             }
12             var polarIndex = serie.polarIndex || 0;
13             var vector = this.component.polar.getVector(polarIndex, dataIndex, 'max');
14             this._event = {
15               zrenderX: vector[0],
16               zrenderY: vector[1]
17             };
18             this._showPolarTrigger(polarIndex, dataIndex);
19           break;
20        }
21 }      

然后在github上查看以前的版本,发现早在echarts 1.4版本中就加入了showTip()hideTip()的功能了,提交链接。
可以看见showTip():

 1 +            if (isAxisTrigger) {
 2  +                // axis trigger 3  +                var dataIndex = params.dataIndex;
 4  +                switch (chart.type) {
 5  +                    case ecConfig.CHART_TYPE_LINE :
 6  +                    case ecConfig.CHART_TYPE_BAR :
 7  +                    case ecConfig.CHART_TYPE_K :
 8  +                        if (typeof xAxis == 'undefined' 
 9  +                            || typeof yAxis == 'undefined'
10  +                            || serie.data.length <= dataIndex
11  +                        ) {
12  +                            return;
13  +                        }
14  +                        var xAxisIndex = serie.xAxisIndex || 0;
15  +                        var yAxisIndex = serie.yAxisIndex || 0;
16  +                        if (xAxis.getAxis(xAxisIndex).type 
17  +                            == ecConfig.COMPONENT_TYPE_AXIS_CATEGORY
18  +                        ) {
19  +                            // 横轴是类目20  +                            _event = {
21  +                                zrenderX : xAxis.getAxis(xAxisIndex).getCoordByIndex(dataIndex),
22  +                                zrenderY : grid.getY() + (grid.getYend() - grid.getY()) / 4
23  +                            };
24  +                        }
25  +                        else {
26  +                            // 纵轴是类目27  +                            _event = {
28  +                                zrenderX : grid.getX() + (grid.getXend() - grid.getX()) / 4,
29  +                                zrenderY : yAxis.getAxis(yAxisIndex).getCoordByIndex(dataIndex)
30  +                            };
31  +                        }
32  +                        _showAxisTrigger(
33  +                            xAxisIndex, 
34  +                            yAxisIndex,
35  +                            dataIndex
36  +                        );
37  +                        break;
38  +                    case ecConfig.CHART_TYPE_RADAR :
39  +                        if (typeof polar == 'undefined' 
40  +                            || serie.data[0].value.length <= dataIndex
41  +                        ) {
42  +                            return;
43  +                        }
44  +                        var polarIndex = serie.polarIndex || 0;
45  +                        var vector = polar.getVector(polarIndex, dataIndex, 'max')
46  +                        _event = {
47  +                            zrenderX : vector[0],
48  +                            zrenderY : vector[1]
49  +                        };
50  +                        _showPolarTrigger(
51  +                            polarIndex, 
52  +                            dataIndex
53  +                        );
54  +                        break;
55  +                }
56  +            }

我顺便找了一下是在2.2.2-到2.2.3版本中被改了。

 

 下面这段代码被改过了:

 1                 switch (chart.type) {
 2                     case ecConfig.CHART_TYPE_LINE :
 3                     case ecConfig.CHART_TYPE_BAR :
 4                     case ecConfig.CHART_TYPE_K :
 5                     //应该只是想删掉这个项,结果把处理程序也删了
 6                     //case ecConfig.CHART_TYPE_TREEMAP :
 7                         if (this.component.xAxis == null 
 8                             || this.component.yAxis == null
 9                             || serie.data.length <= dataIndex
10                         ) {
11                             return;
12                         }
13                         var xAxisIndex = serie.xAxisIndex || 0;
14                         var yAxisIndex = serie.yAxisIndex || 0;
15                         if (this.component.xAxis.getAxis(xAxisIndex).type 
16                             === ecConfig.COMPONENT_TYPE_AXIS_CATEGORY
17                         ) {
18                             // 横轴是类目
19                             this._event = {
20                                 zrenderX: this.component.xAxis.getAxis(xAxisIndex)
21                                           .getCoordByIndex(dataIndex),
22                                 zrenderY: this.component.grid.getY() 
23                                           + (this.component.grid.getYend() 
24                                              - this.component.grid.getY()
25                                             ) / 4
26                             };
27                         }
28                         else {
29                             // 纵轴是类目
30                             this._event = {
31                                 zrenderX: this.component.grid.getX() 
32                                           + (this.component.grid.getXend() 
33                                               - this.component.grid.getX()
34                                             ) / 4,
35                                 zrenderY: this.component.yAxis.getAxis(yAxisIndex)
36                                            .getCoordByIndex(dataIndex)
37                             };
38                         }
39                         this._showAxisTrigger(
40                             xAxisIndex, 
41                             yAxisIndex,
42                             dataIndex
43                         );
44                         break;            

 

OK,把这个段删除的代码复制到你引用的echarts源码tooltip.js中,然后要合并的就重新合并压缩吧。

贴上例子:

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6     <style>
 7         .chart {
 8             height: 500px;
 9             width: 800px;
10         }
11     </style>
12 </head>
13 <body>
14 <div id="chart" class="chart">
15 
16 </div>
17 <span id="hover-console"></span>
18 <span id="console"></span>
19 
20 <script src="./js/jquery.js"></script>
21 <script src="./js/echarts-all.js"></script>
22 <script type="text/javascript">
23     // 基于准备好的dom,初始化echarts图表
24     var myChart = echarts.init(document.getElementById('chart'));
25 
26     var option = {
27         tooltip: {
28             show: true29
29         },
30         legend: {
31             data: ['销量']
32         },
33         xAxis: [
34             {
35                 type: 'category',
36                 data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]
37             }
38         ],
39         yAxis: [
40             {
41                 type: 'value'
42             }
43         ],
44         series: [
45             {
46                 "name": "销量",
47                 "type": "bar",
48                 "data": [5, 20, 40, 10, 10, 20]
49             }
50         ]
51     };
52 
53     // 为echarts对象加载数据
54     myChart.setOption(option);
55     var timer = 0;
56 
57     var total = option.xAxis[0].data.length;
58     var count = 0;
59     var tooltip = myChart.component.tooltip;
60     function autoTip() {
61         timer = setInterval(function () {
62             var curr = count % total;
63 
64             //3.0以上版本的showTip使用方式
65             //myChart.dispatchAction({type: 'showTip', seriesIndex: '1', dataIndex: '1'});
66             tooltip.showTip({seriesIndex: '0', dataIndex: curr});
67             count += 1;
68         }, 1000);
69     }
70     autoTip();
71 
72     var zRender = myChart.getZrender();
73     //mousemove和mouseout总是成对出现,而且out先出现。。。。所以没法解决鼠标hover时暂停自动tip的效果
74     zRender.on('mousemove', function (param) {
75         console.log('move')
76         if (timer) {
77             clearInterval(timer);
78             timer = 0;
79         }
80     });
81     zRender.on('mouseout', function (param) {
82         console.log('OUT');
83         if (param.event) {
84             //判断坐标是否在图表上,然后在处理应该可以实现
85             if (!timer) {
86                 autoTip();
87             }
88         }
89     });
90 </script>
91 </body>
92 </html>

 

补充:

3.0以上的实现方法已经在github共享了代码,可以直接下载使用,如有问题欢迎补充纠正:

https://github.com/chengwubin/echarts-tooltip-cyclic-display.git

 

posted @ 2016-09-02 14:43 不曾潇洒 阅读(...) 评论(...) 编辑 收藏