客戶的需求
客戶是上帝,現在客戶說這個圖表上的點選事件不好操作,特別是在折線上點選時,我無法點中!這時專案經理說,像這樣在空白處有圖例顯示時就能點中可不可以?客戶說,中,我要的就是這個效果!
現實情況
你翻遍了echarts的官方文件,在echarts官方文件的事件欄目中有這樣一段話:在 ECharts 中事件分為兩種型別,一種是使用者滑鼠操作點選,或者 hover 圖表的圖形時觸發的事件,還有一種是使用者在使用可以互動的元件後觸發的行為事件,例如在切換圖例開關時觸發的 legendselectchanged
事件(這裏需要注意切換圖例開關是不會觸發 legendselected
事件的),資料區域縮放時觸發的 datazoom
事件等等。也就是說現實情況其實就是巧婦無米,在事件那塊的api中,所有的事件裡都沒有點選圖表空白地方觸發的,比如click事件,這個只能在點選到圖表要素上時才能觸發;
困局破解
到目前這地步,只看官方文件已經不解渴了,如果你還沒放棄,試著問下AI,它可能會給你提供一些思路,它能幫你省去看原始碼的功夫。在 ECharts 中,點選空白區域(即未繫結資料的區域)預設並不會觸發 click 事件,但是,ECharts 使用 ZRender
作為其渲染引擎,提供了對整個畫布進行事件監聽的能力。你可以透過監聽底層的渲染引擎(ZRender)的 click 事件來捕獲整個畫布上的點選事件,包括空白區域。
這就是說,我可以透過 myChart.getZr() 返回 ZRender 例項,然後監聽它上面的 click 事件,透過這種方式,就可以捕獲到整個圖表區域內的所有點選事件,無論你是否在資料點或圖形上。
我將信將疑地試了下:
this.$refs.flowChat.chart.getZr().on('click', (e) => { debugger })
除錯後發現果然進入了斷點,心裏驚喜萬分,程式設計師的快樂暴擊了。
抓住線索
前面已經找到了這個事件,這還不夠,我得透過這個事件回撥找到對我有用的引數,透過斷點監聽發現回撥包含了這些引數:
有用的引數就是offsetX和offsetY,這兩個是當前滑鼠位置相對整個echart邊框的水平和垂直方向上的偏移量。根據這兩個引數,我就確定了滑鼠的位置,然後在滑鼠所有位置的垂直線上,肯定相交了一條資料,這條資料的x軸就可以拿到了,而我可以寫一個演算法,透過這個x軸的關係找到折線上的真實資料;
還需要一個api
這裏還有一個關鍵的api:convertFromPixel
,在對應官方文件上有說明:轉換畫素座標值到邏輯座標系上的點。也就是說,雖然當前點選位置沒有折線經過,但我也可以透過前面點選獲取的偏移值offsetX和offsetY計算出當前點在座標軸上的真實x、y值,這個x值就是我要的,透過這個x值我可以遍歷y軸對應的series中的值,找到y值。還有一個api要用到,透過echart物件的getOption
方法可以獲取到當前例項的所有option物件,這裏有你想要的series及其data。
// todo this.$refs.flowChat.chart.getZr().on('click', (e) => { debugger getRealData(e.offsetX, e.offsetY) }) getRealData() { const option = myChart.getOption() const series = option.series // todo // 透過e.offsetX和e.offsetY找到series中與點選點相交的那條資料。 }
還需要一個演算法
基本上到這一步,聰明的程式就不需要往下看了,自己動手啪啪啪寫完剩下的了。我這裏還是幫人幫到底,把我的演算法貼出來,即是記錄也可以幫到需要的人。
function getRealData(myChart, x, y) { const option = myChart.getOption() const series = option.series let nearestData = null let minDistance = Infinity series.forEach((s) => { if (s.type === 'line') { const data = s.data data.forEach((item) => { const coord = myChart.convertFromPixel({ seriesIndex: 0 }, [x, y]) const distance = Math.abs(coord[0] - item[0]) if (distance < minDistance) { minDistance = distance nearestData = { seriesName: s.name, data: item, dataIndex: data.indexOf(item) } } }) } }) return nearestData }
透過getOption
獲取到data物件之後,遍歷,然後透過convertFromPixel
逆運算得到我想要的一個座標值(x,y),然後再遍歷data物件,將點選點的x值與data中存的x值相減並求絕對值,透過冒泡演算法尋找,差值最小的就是我要的資料。
還有小技能包喲
這裏要注意一點可能你會有疑問,就是data是一個數組,一般只會存有用的x或者y要素,但其實你在初始化series時,可以在新增了x、y要素之後再push一個完整的包含x、y的物件,這樣我的這個整個流程就成了一個閉環了,因為data是一個數組,透過上面的演算法找到了點選點與折線相交的x座標值就找到了索引然後就能找到對應的完整的x、y了。
ok,到這裏就結束了,盡情的在echart上點選吧,哪怕你瞄不準折線上的點也無所謂,儘管肓點,一定能選中!