vue基于Echarts的拖拽數(shù)據(jù)可視化功能實(shí)現(xiàn)
我司產(chǎn)品提出了一個(gè)需求,做一個(gè)數(shù)據(jù)基于Echars的可拖拽縮放的數(shù)據(jù)可視化,上網(wǎng)百度了一番,結(jié)果出現(xiàn)了兩種結(jié)局,一種花錢買成熟產(chǎn)品(公司不出錢),一種沒有成熟代碼,只能自己寫了,故事即將開始,敬請(qǐng)期待......。 不,還是先上一張效果圖吧,請(qǐng)看......
定義:當(dāng)前元素在屏幕上占用的空間,如下圖:
其中:
offsetHeight: 該元素在垂直方向上的占用的空間,單位為px,不包括margin。
offsetWidth:該元素在水平方向上的占用空間,單位為px,不包括margin。
offsetLeft: 該元素距離左側(cè)并且是定位過(relative || absolute)的父元素內(nèi)邊框的距離。注意:如果父元素中沒有一個(gè)是定位的,則是距離body元素的距離。
offsetTop:該元素距離頂部并且是定位過(relative || absolute)的父元素內(nèi)邊框的距離。注意:如果父元素中沒有一個(gè)是定位的,則是距離body元素的距離。
2. 鼠標(biāo)事件2.1 當(dāng)前鼠標(biāo)的坐標(biāo)點(diǎn)
clientX:返回鼠標(biāo)觸點(diǎn)相對(duì)于瀏覽器可視區(qū)域的X坐標(biāo),單位為px,這個(gè)屬性值可以根據(jù)用戶對(duì)可視區(qū)的縮放行為發(fā)生變化。
clientY:返回鼠標(biāo)觸點(diǎn)相對(duì)于瀏覽器可視區(qū)域的Y坐標(biāo),單位為px,這個(gè)屬性值可以根據(jù)用戶對(duì)可視區(qū)的縮放行為發(fā)生變化。
2.2 相關(guān)的鼠標(biāo)事件
ondragstart: 規(guī)定當(dāng)元素被拖動(dòng)時(shí),發(fā)生什么,該屬性調(diào)用一個(gè)函數(shù),drag(event),它規(guī)定了被拖動(dòng)的數(shù)據(jù)。可通過dataTransfer.setData() 方法設(shè)置被拖數(shù)據(jù)的數(shù)據(jù)類型和值:
function drag(ev){ ev.dataTransfer.setData('Text',ev.target.id);}
ondragover: 規(guī)定在何處放置被拖動(dòng)的數(shù)據(jù),默認(rèn)地,無法將數(shù)據(jù)/元素放置到其他元素中。如果需要設(shè)置允許放置,我們必須阻止對(duì)元素的默認(rèn)處理方式。這要通過調(diào)用 ondragover 事件的 event.preventDefault() 方法:
event.preventDefault()
ondrop:當(dāng)放置被拖數(shù)據(jù)時(shí),會(huì)發(fā)生 drop 事件。ondrop 屬性調(diào)用了一個(gè)函數(shù),drop(event):
function drop(ev){ // 避免瀏覽器對(duì)數(shù)據(jù)的默認(rèn)處理(drop 事件的默認(rèn)行為是以鏈接形式打開) ev.preventDefault(); // 獲得被拖的數(shù)據(jù)。該方法將返回在 setData() 方法中設(shè)置為相同類型的任何數(shù)據(jù)。 var data=ev.dataTransfer.getData('Text'); // 把被拖元素追加到放置元素(目標(biāo)元素)中 ev.target.appendChild(document.getElementById(data));}
onMouseDown: 鼠標(biāo)上的按鈕被按下時(shí)觸發(fā)的事件
onMouseMove:鼠標(biāo)移動(dòng)時(shí)觸發(fā)的事件
onmouseup:鼠標(biāo)按下后,松開時(shí)激發(fā)的事件
拖拽功能本功能以Echarts圖表中柱狀圖為例,進(jìn)行講解:
先定義可拖拽元素
<div> <el-button class='drag-button' type='success' draggable='true' @dragstart.native='dragStart($event,’histogram’)'> 柱狀圖 </el-button> </div>
注意:元素默認(rèn)是不能進(jìn)行拖拽的,需要將draggable屬性設(shè)置為'true',即draggable='true'
dragStart(event,type){ event.dataTransfer.setData('Text',type); },
定義放置區(qū)域
<div @drop.prevent='drop($event)' @dragover.prevent=''> <div id='’histogram’></div></div>
其中drop事件如下:
drop(event){ const data=event.dataTransfer.getData('Text'); if(data === ’histogram’){ var myChart = echarts.init(document.getElementById(’histogram’)); // 指定圖表的配置項(xiàng)和數(shù)據(jù) var option = { title: { text: ’ECharts 入門示例’ }, tooltip: {}, legend: { data:[’銷量’] }, xAxis: { data: ['襯衫','羊毛衫','雪紡衫','褲子','高跟鞋','襪子'] }, yAxis: {}, series: [{ name: ’銷量’, type: ’bar’, data: [5, 20, 36, 10, 10, 20] }] }; // 使用剛指定的配置項(xiàng)和數(shù)據(jù)顯示圖表。 myChart.setOption(option); }}
基于自己封裝的組件的源碼請(qǐng)看github:github地址
效果圖如下:
此功能用到了上面提到的offsetLeft、offsetTop、clientX,clientY。實(shí)現(xiàn)思路如下:
(1)當(dāng)鼠標(biāo)剛按下去時(shí),記錄當(dāng)前元素距離帶定位的父元素的offsetLeft、offsetTop距離;以及當(dāng)前鼠標(biāo)在瀏覽器可視區(qū)的坐標(biāo)clientX、clientY的距離。
(2)分別計(jì)算兩者的差值作為偏移常量,如下:
let disX = e.clientX - el.offsetLeft;let disY = e.clientY - el.offsetTop;
(3)監(jiān)聽鼠標(biāo)的移動(dòng)事件,獲取當(dāng)前鼠標(biāo)距離瀏覽器可是區(qū)域的坐標(biāo)clientX,clientY;然后減去偏移常量,即為當(dāng)前元素的坐標(biāo)
let tX = e.clientX - disX; let tY = e.clientY - disY; el.style.left = tX + ’px’; el.style.top = tY + ’px’;
說明:el為當(dāng)前圖表對(duì)象
由于本人封裝了通用vue組件,詳細(xì)拖拽方法代碼如下:
// 初始化可拖拽方法 initDrag(){ let el = this.$el; el.onmousedown = (e)=>{ e.preventDefault(); e.target.style.cursor = ’move’; //鼠標(biāo)按下,計(jì)算鼠標(biāo)觸點(diǎn)距離元素左側(cè)和頂部的距離 let disX = e.clientX - el.offsetLeft; let disY = e.clientY - el.offsetTop; // console.log(’22222’,document); document.onmousemove = function (e) { //計(jì)算需要移動(dòng)的距離 let tX = e.clientX - disX; let tY = e.clientY - disY; //移動(dòng)當(dāng)前元素 if (tX >= 0 && tX <= window.innerWidth - el.offsetWidth) {el.style.left = tX + ’px’; } if (tY >= 0 && tY <= window.innerHeight - el.offsetHeight) {el.style.top = tY + ’px’; } }; //鼠標(biāo)松開時(shí),注銷鼠標(biāo)事件,停止元素拖拽。 document.onmouseup = function (e) {document.onmousemove = null;document.onmouseup = null;e.target.style.cursor = ’default’; }; } },
如果想看封裝的組件,請(qǐng)查看github地址:github地址
拖動(dòng)效果如下圖:
此功能用到了上面提到的 offsetWidth、offsetHeight、offsetLeft、offsetTop、clientX,clientY。實(shí)現(xiàn)思路如下:
(1)先設(shè)置可縮放的四個(gè)拖拽方框
<div v-show='isResize && resizeFlag' ref='resizeDivTag' id='resizeDivTag'><span class='br'></span><span class='bl'></span><span class='tr'></span><span class='tl'></span> </div>
其中isResize 與 resizeFlag表示是否可縮放以及是否可拖拽
(2)為每個(gè)可可縮放方框設(shè)置縮放函數(shù),請(qǐng)看注釋
// 初始化可縮放 initResize(){let el = this.$el; //獲取當(dāng)前元素let spanNodes = this.$refs.resizeDivTag.childNodes;for(let i=0;i<spanNodes.length;i++){ this.resizeElementFun(spanNodes[i],el); // 分別為四個(gè)縮放方框設(shè)置監(jiān)聽事件} }, resizeElementFun(element,el){element.onmousedown = function(ev){ console.log(’我是按下的元素’) let oEv = ev || event; oEv.stopPropagation(); let oldWidth = el.offsetWidth; // 當(dāng)前元素的寬度 let oldHeight = el.offsetHeight; // 當(dāng)前元素的高度 console.log(’-----’+ oldWidth+’----’+oldHeight); let oldX = oEv.clientX; // 當(dāng)前鼠標(biāo)對(duì)于瀏覽器可視區(qū)域的X坐標(biāo) let oldY = oEv.clientY; // 當(dāng)前鼠標(biāo)對(duì)于瀏覽器可視區(qū)域的Y坐標(biāo) let oldLeft = el.offsetLeft; // 當(dāng)前元素對(duì)于父級(jí)定位元素的寬度 let oldTop = el.offsetTop; // 當(dāng)前元素對(duì)于父級(jí)定位元素的高度 console.log(’--zuo---’+ oldLeft+’--gao--’+oldTop); document.onmousemove = function(ev){ // oEv.stopPropagation(); let oEv = ev || event; let disY = (oldTop + (oEv.clientY - oldY)); // 當(dāng)前縮放的元素距離定位父元素的高度 // let disX = (oldLeft + (oEv.clientX - oldLeft)); let disX = (oldLeft + (oEv.clientX - oldX)); // 當(dāng)前縮放的元素距離定位父元素的寬度 if(disX>oldLeft+oldWidth){disX=oldLeft+oldWidth } if(disY>oldTop+oldHeight){disY=oldTop+oldHeight } if(element.className == ’tl’){ // 左上縮放時(shí) el.style.width = oldWidth - (oEv.clientX - oldX) + ’px’; // 元素寬度= 縮放前寬度-鼠標(biāo)當(dāng)前坐標(biāo)與原始坐標(biāo)差值 el.style.height = oldHeight - (oEv.clientY - oldY) + ’px’; // 元素寬度= 縮放前高度-鼠標(biāo)當(dāng)前坐標(biāo)與原始坐標(biāo)差值 el.style.left = disX + ’px’; // 元素距離父元素的距離 = el.style.top = disY + ’px’; } else if (element.className == ’bl’){ // 左下 el.style.width = oldWidth - (oEv.clientX - oldX) + ’px’; el.style.height = oldHeight + (oEv.clientY - oldY) + ’px’; el.style.left = disX + ’px’; // el.style.bottom = oldTop + (oEv.clientY + oldY) + ’px’; } else if (element.className == ’tr’){ //右上 el.style.width = oldWidth + (oEv.clientX - oldX) + ’px’; el.style.height = oldHeight - (oEv.clientY - oldY) + ’px’; el.style.right = oldLeft - (oEv.clientX - oldX) + ’px’; el.style.top = disY + ’px’; } else if (element.className == ’br’){ //右下 el.style.width = oldWidth + (oEv.clientX - oldX) + ’px’; el.style.height = oldHeight + (oEv.clientY - oldY) + ’px’; el.style.right = oldLeft - (oEv.clientX - oldX) + ’px’; // el.style.bottom = oldTop + (oEv.clientY + oldY) + ’px’; } } document.onmouseup = function(){ document.onmousemove = null; }; return false;} },
如果想看封裝的組件,請(qǐng)查看github地址:github地址
縮放效果如下圖:
Echarts縮放中存在的問題
vue中使用echarts圖表自適應(yīng)的幾種基本解決方案,此處不再進(jìn)行贅述,詳情請(qǐng)參考如下鏈接:echarts圖表自適應(yīng)
結(jié)束語本文只是粗略的記錄了數(shù)據(jù)可視化簡(jiǎn)單demo的實(shí)現(xiàn)思路,如果您覺得對(duì)您有幫助,請(qǐng)下載源碼 github地址
喜歡的小伙伴給個(gè)star,謝謝
參考文獻(xiàn)https://www.jb51.net/article/201371.htm
https://www.jb51.net/article/201349.htm
到此這篇vue基于Echarts的拖拽數(shù)據(jù)可視化功能實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)vue基于Echarts拖拽數(shù)據(jù)可視化內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!
相關(guān)文章:
1. JavaWeb Servlet中url-pattern的使用2. jsp中sitemesh修改tagRule技術(shù)分享3. asp(vbscript)中自定義函數(shù)的默認(rèn)參數(shù)實(shí)現(xiàn)代碼4. React優(yōu)雅的封裝SvgIcon組件示例5. 輕松學(xué)習(xí)XML教程6. php網(wǎng)絡(luò)安全中命令執(zhí)行漏洞的產(chǎn)生及本質(zhì)探究7. ASP刪除img標(biāo)簽的style屬性只保留src的正則函數(shù)8. JSP servlet實(shí)現(xiàn)文件上傳下載和刪除9. ASP基礎(chǔ)知識(shí)VBScript基本元素講解10. 詳解瀏覽器的緩存機(jī)制
