最近需要實現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>
當鼠標觸發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-auto-show