摘要:上篇原生繪制餅圖介紹了如何使用來繪制環圖,這篇用標簽來實現一下。在標簽里,插入其它元素,來進行繪圖元素用于定義一個路徑,使用標簽來繪制每項數據的環圖份額元素用于定義一條曲線,使用繪制每項數據的線條元素用于定義文本使用顯示數據的文本信息。
上篇<原生Canvas繪制餅圖>介紹了如何使用Canvas來繪制環圖,這篇用SVG標簽來實現一下。
上面是完整效果圖,下面來看看具體實現。
使用的SVG元素:SVG代碼的開始標簽,相當于創建一個畫布。在svg標簽里,插入其它SVG元素,來進行繪圖;
以上是會用到的幾個SVG標簽,詳細說明可以看看菜鳥教程SVG或者MDN的SVG教程
SVG是一種用來描述二維矢量圖形的XML標記語言,所以SVG標簽不能使用document.createElement直接創建(瀏覽器無法識別)。需要使用document.createElementNS創建一個具有指定的命名空間URI和限定名稱的元素,SVG的命名空間是"http://www.w3.org/2000/svg"。這里將創建SVG標簽操作寫在了createSvgTag函數里。下面先新建一個svg元素:
/** * 將創建環圖的所有操作寫在drawPie函數內,配置一些默認參數 * @param {Element} element [插入SVG的元素,缺省直接插入到body] * @param {Number} R [外弧起終點計算半徑] * @param {Number} r [內弧起終點計算半徑] * @param {Number} width [畫布寬度] * @param {Number} height [畫布高度] * @param {Array} data [圖表數據] */ function drawPie({element, R = 140, r = 100,width = 450,height = 400,data = []} = {}) { let w = width; let h = height; //將width、height賦值給w、h let origin = [w / 2, h / 2]; //以畫布的中心點,作為環圖的原點 //創建一個svgs標簽 let svg = createSvgTag("svg", { "width": w + "px", "height": h + "px", "viewBox": `0 0 ${w} ${h}`, }); (element && element.appendChild) ? element.appendChild(svg) : document.body.appendChild(svg);//插入到DOM /** * 將創建SVG標簽寫成一個函數 * @param {tring} tagName [標簽名] * @param {Object} attributes [標簽屬性] * @return {Element} svg標簽 */ function createSvgTag (tagName, attributes) { let tag = document.createElementNS("http://www.w3.org/2000/svg", tagName) for (let attr in attributes) { tag.setAttribute(attr, attributes[attr]) } return tag; } }) //調用 drawPie({ data: [{ cost: 4.94, category: "通訊", color: "#e95e45", }, { cost: 4.78, category: "服裝美容", color: "#20b6ab", }, { cost: 4.00, category: "交通出行", color: "#ef7340", }, { cost: 3.00, category: "飲食", color: "#eeb328", }, { cost: 49.40, category: "其他", color: "#f79954", }, { cost: 28.77, category: "生活日用", color: "#00a294", }] })
我們會用到的指令有:
Moveto(移動畫筆到起始點),語法:"M x,y" 在這里x和y是絕對坐標,分別代表水平坐標和垂直坐標;
Lineto(繪制直線),語法:"L x, y" 在這里x和y是絕對坐標,表示直線的結束點坐標;
Arcto(繪制弧曲線路徑),語法:"A rx,ry xAxisRotate LargeArcFlag,SweepFlag x,y",rx和ry分別是x和y方向的半徑(繪制圓弧時,rx和ry相等);LargeArcFlag的值確定是要畫小弧或大弧,0表示畫小弧(即畫兩點之間弧長最小的弧),1表示畫大弧(當弧度大于Math.PI時需要畫大弧);SweepFlag用來確定畫弧的方向,0逆時針方向,1順時針方向;x和y是目的地的坐標;
ClosePath(閉合路徑),語法是"Z"或"z";
詳情MDN path元素d屬性。
我們需要用path繪制如下的路徑:
如圖:份額的繪制是先使用M命令移動到P0,L命令繪制一條直線到P1,A命令從P1畫弧到P2,L命令從P2繪制一條直線到P3,A命令從P3繪制一條弧線到P0,最后Z命令關閉路徑。然后我們只要填充這個路徑就可以完成每項份額繪制了。這里我們需要求出4個點的絕對坐標,如何計算這四個坐標?
如圖,通過三角函數,我們就可以計算出每個點的位置。我們已知原點O坐標(畫布中點)、外環半徑R和內環半徑r(我們自己給定);再通過計算出每項數據的弧度占比,我們就可以得到每項數據的起始弧度(即上一項的結束弧度,第一項為0)和結束弧度(起點+項數據的弧度占比)。x值為原點x+sin(angel)半徑,y值為原點y-cos(angel)半徑
這里將計算點坐標的運算寫在evaluateXY函數中,如下:
/** * 計算Xy坐標 * @param {[type]} r [半徑] * @param {[type]} angel [角度] * @param {[type]} origin [原點坐標] * @return {[Array]} 坐標 */ function evaluateXY (r, angel, origin) { return [origin[0] + Math.sin(angel) * r, origin[0] - Math.cos(angel) * r] }
接下來,我們遍歷數據,計算出每項數據的四個點:
//遍歷計算每項數據 for(let v of data) { let itemData = Object.assign({}, v);//copy一遍,不直接修改原數據 eAngel = sAngel + v.cost / total * 2 * Math.PI; //計算結束弧度 itemData.arclineStarts = [ evaluateXY(r, sAngel, origin), //計算P0坐標 evaluateXY(R, sAngel, origin), //計算P1坐標 evaluateXY(R, eAngel, origin), //計算P2坐標 evaluateXY(r, eAngel, origin) //計算P3坐標 ]; itemData.LargeArcFlag = (eAngel - sAngel) > Math.PI ? "1" : "0";//大于Math.PI需要畫大弧,否則畫小弧 drawData.push(itemData);//保存到drawData數組中,繪制時遍歷 sAngel = eAngel;//將下一項數據的起始弧度設置為當前項的結束弧度 }
下面,遍歷drawData,繪制環圖:
//遍歷計算每項數據,進行繪制 for(let v of drawData) { let P = v.arclineStarts; let path = createSvgTag("path", { "fill": v.color, //設置填充色 "stroke": "black", "stroke-width": "0", //畫筆大小為零 /** * d屬性設置路徑字符串 * M ${P[0][0]} ${P[0][1]} 移動畫筆到P0點 * L ${P[1][0]} ${P[1][1]} 繪制一條直線到P1 * A ${R} ${R} 0 ${v.LargeArcFlag} 1 ${P[2][0]} ${P[2][1]} 繪制外環弧到P2,R為外半徑,v.LargeArcFlag畫大弧還是小弧 * L ${P[3][0]} ${P[3][1]} 繪制一條直線到P3 * A ${r} ${r} 0 ${v.LargeArcFlag} 0 ${P[0][0]} ${P[0][1]} 繪制內環弧到P0點 * z 關閉路徑 */ "d": `M ${P[0][0]} ${P[0][1]} L ${P[1][0]} ${P[1][1]} A ${R} ${R} 0 ${v.LargeArcFlag} 1 ${P[2][0]} ${P[2][1]} L ${P[3][0]} ${P[3][1]} A ${r} ${r} 0 ${v.LargeArcFlag} 0 ${P[0][0]} ${P[0][1]} z` }) svg.appendChild(path); //添加到畫布中 }
到這里,就已經繪制出如下環圖了:
記得還需要把相關變量先聲明一下。
下面我們需要繪制線條和文字。
起點坐標:這里設置起點為每項數據的弧線中間位置,通過計算中間位置對應的弧度,就可以通過三角函數計算出這個點坐標;
線束點坐標:當線條起點在右側時,線束點就是垂直平行起點圖表最右側位置;當線條起點在左側時,線束點就是垂直平行起點圖表最右左位置;假設起點為[sx,sy],右左結束點應該就是[w,sy]、[0,sy],w為圖表寬度;
折點:需要處理數據會擠在一起的情況,就會改變結束點坐標的y值,當起點和結束點y值不相等時,就需要設置折點。
以下是完整代碼:
svg-pie
也可以查看gitee。
總結一下難點:難的地方就在弧上各點的計算,需要好好再回憶一下數學的三角函數。
對比canvas:canvas只需有一個標簽,svg實現就在DOM中增加了一堆標簽。這樣一來,svg的優勢就在于第項都是一個標簽,你可以直接針對這個標簽要綁定事件和做修改。比如要實現鼠標稱到某個項,放大這個項,svg只要給每個path綁定事件,修改當前的這個path就行;而canvas只能在canvase綁定事件,先通過計算鼠標位置來判斷移動到了哪個份額上,然后再重繪整個canvas;同樣,標簽過多也是svg的缺點,我們這點標簽其實沒什么,一旦標簽多起來,肯定是會給瀏覽器渲染帶來負擔的。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/94533.html
摘要:可以用于模型化許多不同種類的信息,因此將一個數據結構可視化的需求也變得非常普遍。并且由于大部分圖的數據都非常復雜甚至動態變化,所以自動可配置的可視化布局算法顯然比人為排版更為高效且可靠。 有向無環圖(DAG)布局 有向無環圖及其布局算法 有向無環圖(directed acyclic graph,以下簡稱 DAG)是一種常見的圖形,其具體定義為一種由有限個頂點和有限條帶有方向的邊組成的圖...
摘要:判斷是否成環執行拓撲排序,如果序列中的頂點數不等于有向圖的頂點個數,則說明圖中存在環。如果訪問過,且不是其父節點,那么就構成環圖有向無環圖的最小路徑覆蓋圖存儲鄰接矩陣圖的鄰接矩陣存儲方式是用兩個數組來表示圖。 何為有向無環圖? 1、首先它是一個圖,然后它是一個有向圖,其次這個有向圖的任意一個頂點出發都沒有回到這個頂點的路徑,是為有向無環圖2、DAG(Directed Acyclic G...
摘要:本文適合適合對繪制圖形學前端可視化感興趣的讀者閱讀。結論已經明顯瀏覽器下,用下繪制繪制圖的時候,的設置將不生效。下面是一段用于測試的代碼,表示用源圖像的形狀去挖空目標圖像。后續繪制用臨時的替代圖片。 本文適合適合對canvas繪制、圖形學、前端可視化感興趣的讀者閱讀。 楔子 所有的事情都會有一個起因。最近產品上需要做一個這樣的功能:給一些圖形進行染色處理。想想這還不是順手拈來的事情,早...
閱讀 1626·2021-10-25 09:46
閱讀 3209·2021-10-08 10:04
閱讀 2354·2021-09-06 15:00
閱讀 2768·2021-08-19 10:57
閱讀 2077·2019-08-30 11:03
閱讀 970·2019-08-30 11:00
閱讀 2370·2019-08-26 17:10
閱讀 3545·2019-08-26 13:36