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