故事背景:這幾天遇到一個客戶,是做會議記錄的,每次會議過程中,都會有特定設(shè)備記錄下講話人的位置以角度值顯示。他給我角度值,讓我給他做一個圖表來展示每個人的一個大概位置。
客戶想到的是用 Echarts 圖表來做,我首先想到的也是用 Echarts ,但是思考了他的要求以后,發(fā)現(xiàn)就一個簡單的框選圖表用 Echarts 來做是不是大材小用了,而且還要導(dǎo)入那么多的沒用的代碼。
于是我想到了用 canvas 畫布來仿著做,但又考慮了一下, canvas 操作起來不順手;究竟可不可以用普通的css結(jié)合 javascript 來把它做出來呢?此番思考驗證了:任何事情一定要多動腦,才能 碰到更簡單的解決問題的方式。
考慮到也許某天大家用得著,所以發(fā)布出來。注:擁有可移植性,可移到頁面任何位置,效果不會改變
先看最終效果吧:
圖一:
圖二:
這個小東西會涉及的知識點不多,歸納一下: js的三角函數(shù) 、 CSS3的transform 、 鼠標的坐標軸XY的計算 ...啊哈,差不多大體就這三方面的知識吧,如果你都只是有過了解也沒關(guān)系,因為都只用的到皮毛所以不必擔心。但是如果完全沒聽過,那就請您再去了解一下這方面知識。
代碼區(qū)域
<!doctype html> <html> <head> <meta charset="utf-8" /> <title>仿Echarts圖表</title> <style> * { padding:0; margin:0; } #getcharts { position:relative; width:510px; height:510px; } #wrapcharts { list-style:none; height:500px; width:500px; border:2px solid #aaa; border-radius:50%; position:relative; margin:20px auto; } #wrapcharts li { height:10px; width:10px; diaplay:block; position:absolute; cursor:pointer; left:247px; top:2px; height:10px; width:10px; transition:0.2s; background:red; border-radius:50%; } #boxshadow { position:absolute; background:blue; opacity:0.2; height:0; width:0; left:0; top:0; } </style> </head> <body> <ul id="wrapcharts"></ul> <div id="boxshadow"></div> <script> /* **模擬從后端取值過來的【角度】和相對應(yīng)的【人名】數(shù)組 **/ var degArr = [25,88,252,323,33,28,30,90,290,100,300,50,180,205,220,331,195,97,102,77,62,38,32,79]; var nameArr = ['內(nèi)衣天使','小惡魔','金正恩','奧巴馬','duolaA夢','午夜激情','梁靜茹','劉亦菲','琪琪','大熊','小靜','小屁孩','張三','李四','王五','麻六','小明','小張','麗麗','多多','瑾瑾','biubiu','Mr.boluo','Hanson']; /* **聲明 getPos(param)函數(shù): 利用三角函數(shù)定理根據(jù)傳入的角度值獲取對邊和臨邊的x,y值 **/ function getPos(deg) { var X = Math.sin(deg*Math.PI/180)*250 + 245; var Y = -Math.cos(deg*Math.PI/180)*250 + 245; return {x:X,y:Y}; } /* **這里不用說吧,獲取頁面中的ul,和ul中的li對象,以及框選時的那個任意變動大小的小方塊對象 **/ var oWrap = document.getElementById('wrapcharts'); var aLi = oWrap.getElementsByTagName('li'); var oBox =document.getElementById('boxshadow'); var allLi = ''; var posArr = []; /* **for循環(huán)中調(diào)用getPos(param)來獲取degArr數(shù)組中的所有角度對應(yīng)的x,y值(就是每個角度對應(yīng)的x,y坐標),并傳入到一個數(shù)組中保存,方便取用 **/ for(var i=0;i<degArr.length; i++) { posArr.push(getPos(degArr[i])); } /* **for循環(huán)根據(jù)度數(shù)數(shù)組degArr的長度插入li小圓點到ul中,并將之前獲取的每個點對應(yīng)的x,y左邊插入到行內(nèi)樣式 **/ for(var i=0; i<degArr.length; i++) { allLi += '<li style="left:'+posArr[i].x+'px;top:'+posArr[i].y+'px;" title="'+degArr[i]+'°;姓名:'+nameArr[i]+'"></li>'; } oWrap.innerHTML = allLi; /* **遍歷最終得到的ul中的li **/ for(var i=0; i<aLi.length; i++) { aLi[i].index = i; /* **封裝鼠標移入每個小圓點時的放大事件,這里用到了matrix矩陣,為的事想兼容ie9以下瀏覽器,但是好像出了點問題 */ function focusOn(_this,color, size) { _this.style.background = color; _this.style.WebkitTransform = 'matrix('+size+', 0, 0, '+size+', 0, 0)'; _this.style.MozTransform = 'matrix('+size+', 0, 0, '+size+', 0, 0)'; _this.style.transform = 'matrix('+size+', 0, 0, '+size+', 0, 0)'; _this.style.filter="progid:DXImageTransform.Microsoft.Matrix( M11= "+size+", M12= 0, M21= 0 , M22="+size+",SizingMethod='auto expend')"; } aLi[i].onmouseover = function() { //alert(this.offsetLeft); _this = this; focusOn(_this,'blue', 2); } aLi[i].onmouseout = function() { //alert(this.offsetLeft); _this = this; focusOn(_this,'red', 1); } } /***框選***/ /* **拖拽框選代碼區(qū)域,這個我就不解釋了,明白人都一眼知道什么意思,這就像是公式, */ var allSelect = {}; document.onmousedown = function(ev) { var ev = ev || window.event; var disX = ev.clientX; var disY = ev.clientY; var H = W = clientleft = clienttop = clientright = clientbottom = 0; oBox.style.cssText = 'left:'+disX+'px;top:'+disY+'px;'; //console.log(disX+';'+disY); function again(f) { for(var i=0; i<posArr.length; i++) { if(posArr[i].x > clientleft && posArr[i].y > clienttop && (posArr[i].x + 10) < clientright && (posArr[i].y +10) < clientbottom) { //console.log(clientleft+';'+ clienttop +';'+ clientright +';' + clientbottom); if(f){allSelect[i] = i;}else{ aLi[i].style.background = 'blue'; } } else { aLi[i].style.background = 'red'; } } } document.onmousemove = function(ev) { var ev = ev || window.event; /* **當鼠標向四個方向拖拉的時候進行方向判斷,并相應(yīng)的改變小方塊的left,top以及width,height **其實我這里有個問題,那就是,代碼重復(fù)了一些,本想著合并一下,但是作者有點懶,嘿嘿,你們也可以嘗試一下 **修改后你們拿去當做你們的發(fā)布,作者不會介意的 */ if(ev.clientX > disX && ev.clientY > disY) { W = ev.clientX - disX; H = ev.clientY - disY; oBox.style.width = W + 'px'; oBox.style.height = H + 'px'; clienttop = disY-oWrap.offsetTop; clientleft = disX-oWrap.offsetLeft; }else if(ev.clientX < disX && ev.clientY < disY) { W = disX - ev.clientX; H = disY - ev.clientY; oBox.style.top = ev.clientY + 'px'; oBox.style.left = ev.clientX + 'px'; oBox.style.width = W + 'px'; oBox.style.height = H + 'px'; clienttop = ev.clientY - oWrap.offsetTop; clientleft = ev.clientX - oWrap.offsetLeft; }else if(ev.clientX > disX && ev.clientY < disY) { W = ev.clientX - disX; H = disY - ev.clientY; oBox.style.top = ev.clientY + 'px'; oBox.style.width = W + 'px'; oBox.style.height = H + 'px'; clienttop = ev.clientY - oWrap.offsetTop; clientleft = disX - oWrap.offsetLeft; }else if(ev.clientX < disX && ev.clientY > disY) { W = disX - ev.clientX; H = ev.clientY - disY; oBox.style.left = ev.clientX + 'px'; oBox.style.width = W + 'px'; oBox.style.height = H + 'px'; clienttop = disY-oWrap.offsetTop; clientleft = ev.clientX - oWrap.offsetLeft; } clientright = clientleft+ W; clientbottom = clienttop + H; W = ''; H = ''; again(); } document.onmouseup = function() { again(1); document.onmouseup = document.onmousemove = null; oBox.style.cssText = 'height:0;width:0;'; if(JSON.stringify(allSelect) == '{}'){return;} console.log(allSelect); var lastSelect = []; for(var attr in allSelect){ lastSelect.push(nameArr[attr]); } allSelect = {}; console.log(lastSelect); alert('你選中的人是:\n\n'+lastSelect+'\n\n'); for(var i=0; i<aLi.length; i++) { aLi[i].style.background = 'red'; } } return false; } </script> </body> </html>
會用到的一些知識點拓展
注:在js中設(shè)置Transform的時候我用到的不是scale()方法,因為我想兼容ie9以下的版本所以用了矩陣變化。當然,你們也可以改為scale(),毫無影響。
1.在標準瀏覽器下的矩陣函數(shù)matix(a,b,c,d,e,f)、ie下的矩陣函數(shù)progid:DXImageTransform.Microsoft.Matrix( M11= 1, M12= 0, M21= 0 , M22=1,SizingMethod='auto expend')
他們的共同點:M11 == a; M12 == c; M21 == b; M22 == d