echarts实现自动轮播tooltip
最近需要实现echarts图形中hover效果轮播(即tooltip在各个数据点上轮流显示)的功能,以下就是我学习的一个过程,只是提供思路,具体场景需要自己修改。(仅针对echarts 2.2.7及以下版本,最后的代码有3.0以上的使用方法以及插件代码链接)
源码:https://github.com/chengwubin/echarts-tooltip-auto-show
关于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>
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-auto-show