成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

SVG繪制環(huán)圖

AlphaWallet / 1701人閱讀

摘要:上篇原生繪制餅圖介紹了如何使用來(lái)繪制環(huán)圖,這篇用標(biāo)簽來(lái)實(shí)現(xiàn)一下。在標(biāo)簽里,插入其它元素,來(lái)進(jìn)行繪圖元素用于定義一個(gè)路徑,使用標(biāo)簽來(lái)繪制每項(xiàng)數(shù)據(jù)的環(huán)圖份額元素用于定義一條曲線(xiàn),使用繪制每項(xiàng)數(shù)據(jù)的線(xiàn)條元素用于定義文本使用顯示數(shù)據(jù)的文本信息。

上篇<原生Canvas繪制餅圖>介紹了如何使用Canvas來(lái)繪制環(huán)圖,這篇用SVG標(biāo)簽來(lái)實(shí)現(xiàn)一下。

上面是完整效果圖,下面來(lái)看看具體實(shí)現(xiàn)。

使用的SVG元素

:SVG代碼的開(kāi)始標(biāo)簽,相當(dāng)于創(chuàng)建一個(gè)畫(huà)布。在svg標(biāo)簽里,插入其它SVG元素,來(lái)進(jìn)行繪圖;

: path元素用于定義一個(gè)路徑,使用path標(biāo)簽來(lái)繪制每項(xiàng)數(shù)據(jù)的環(huán)圖份額;

:polyline元素用于定義一條曲線(xiàn),使用polyline繪制每項(xiàng)數(shù)據(jù)的線(xiàn)條;

: text元素用于定義文本,使用text顯示數(shù)據(jù)的文本信息。

以上是會(huì)用到的幾個(gè)SVG標(biāo)簽,詳細(xì)說(shuō)明可以看看菜鳥(niǎo)教程SVG或者M(jìn)DN的SVG教程

標(biāo)簽創(chuàng)建畫(huà)布

SVG是一種用來(lái)描述二維矢量圖形的XML標(biāo)記語(yǔ)言,所以SVG標(biāo)簽不能使用document.createElement直接創(chuàng)建(瀏覽器無(wú)法識(shí)別)。需要使用document.createElementNS創(chuàng)建一個(gè)具有指定的命名空間URI和限定名稱(chēng)的元素,SVG的命名空間是"http://www.w3.org/2000/svg"。這里將創(chuàng)建SVG標(biāo)簽操作寫(xiě)在了createSvgTag函數(shù)里。下面先新建一個(gè)svg元素:

/**
 * 將創(chuàng)建環(huán)圖的所有操作寫(xiě)在drawPie函數(shù)內(nèi),配置一些默認(rèn)參數(shù)
 * @param  {Element} element [插入SVG的元素,缺省直接插入到body]
 * @param  {Number}  R       [外弧起終點(diǎn)計(jì)算半徑]
 * @param  {Number}  r       [內(nèi)弧起終點(diǎn)計(jì)算半徑]
 * @param  {Number}  width   [畫(huà)布寬度]
 * @param  {Number}  height  [畫(huà)布高度]
 * @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à)布的中心點(diǎn),作為環(huán)圖的原點(diǎn)
    
    //創(chuàng)建一個(gè)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)簽寫(xiě)成一個(gè)函數(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",
    }]
})
繪制每項(xiàng)數(shù)據(jù)的環(huán)圖份額

元素的屬性d用于定義路徑,屬性d實(shí)際上是一個(gè)字符串,包含了一系列路徑描述。這些路徑由下面這些指令組成:Moveto,Lineto,Curveto,Arcto,ClosePath。
我們會(huì)用到的指令有:

Moveto(移動(dòng)畫(huà)筆到起始點(diǎn)),語(yǔ)法:"M x,y" 在這里x和y是絕對(duì)坐標(biāo),分別代表水平坐標(biāo)和垂直坐標(biāo);

Lineto(繪制直線(xiàn)),語(yǔ)法:"L x, y" 在這里x和y是絕對(duì)坐標(biāo),表示直線(xiàn)的結(jié)束點(diǎn)坐標(biāo);

Arcto(繪制弧曲線(xiàn)路徑),語(yǔ)法:"A rx,ry xAxisRotate LargeArcFlag,SweepFlag x,y",rx和ry分別是x和y方向的半徑(繪制圓弧時(shí),rx和ry相等);LargeArcFlag的值確定是要畫(huà)小弧或大弧,0表示畫(huà)小弧(即畫(huà)兩點(diǎn)之間弧長(zhǎng)最小的弧),1表示畫(huà)大弧(當(dāng)弧度大于Math.PI時(shí)需要畫(huà)大弧);SweepFlag用來(lái)確定畫(huà)弧的方向,0逆時(shí)針?lè)较颍?順時(shí)針?lè)较?x和y是目的地的坐標(biāo);

ClosePath(閉合路徑),語(yǔ)法是"Z"或"z";

詳情MDN path元素d屬性。
我們需要用path繪制如下的路徑:

如圖:份額的繪制是先使用M命令移動(dòng)到P0,L命令繪制一條直線(xiàn)到P1,A命令從P1畫(huà)弧到P2,L命令從P2繪制一條直線(xiàn)到P3,A命令從P3繪制一條弧線(xiàn)到P0,最后Z命令關(guān)閉路徑。然后我們只要填充這個(gè)路徑就可以完成每項(xiàng)份額繪制了。這里我們需要求出4個(gè)點(diǎn)的絕對(duì)坐標(biāo),如何計(jì)算這四個(gè)坐標(biāo)?

如圖,通過(guò)三角函數(shù),我們就可以計(jì)算出每個(gè)點(diǎn)的位置。我們已知原點(diǎn)O坐標(biāo)(畫(huà)布中點(diǎn))、外環(huán)半徑R和內(nèi)環(huán)半徑r(我們自己給定);再通過(guò)計(jì)算出每項(xiàng)數(shù)據(jù)的弧度占比,我們就可以得到每項(xiàng)數(shù)據(jù)的起始弧度(即上一項(xiàng)的結(jié)束弧度,第一項(xiàng)為0)和結(jié)束弧度(起點(diǎn)+項(xiàng)數(shù)據(jù)的弧度占比)。x值為原點(diǎn)x+sin(angel)半徑,y值為原點(diǎn)y-cos(angel)半徑
這里將計(jì)算點(diǎn)坐標(biāo)的運(yùn)算寫(xiě)在evaluateXY函數(shù)中,如下:

/**
 * 計(jì)算Xy坐標(biāo)
 * @param  {[type]} r      [半徑]
 * @param  {[type]} angel  [角度]
 * @param  {[type]} origin [原點(diǎn)坐標(biāo)]
 * @return {[Array]} 坐標(biāo)
 */
function evaluateXY (r, angel, origin) {
    return [origin[0] + Math.sin(angel) * r, origin[0] - Math.cos(angel) * r]                                                                                  
}

接下來(lái),我們遍歷數(shù)據(jù),計(jì)算出每項(xiàng)數(shù)據(jù)的四個(gè)點(diǎn):

//遍歷計(jì)算每項(xiàng)數(shù)據(jù)
for(let v of data) {
    let itemData = Object.assign({}, v);//copy一遍,不直接修改原數(shù)據(jù)
    eAngel = sAngel + v.cost / total * 2 * Math.PI; //計(jì)算結(jié)束弧度
    itemData.arclineStarts = [
        evaluateXY(r, sAngel, origin), //計(jì)算P0坐標(biāo)
        evaluateXY(R, sAngel, origin), //計(jì)算P1坐標(biāo) 
        evaluateXY(R, eAngel, origin), //計(jì)算P2坐標(biāo) 
        evaluateXY(r, eAngel, origin)  //計(jì)算P3坐標(biāo)
        ];

    itemData.LargeArcFlag = (eAngel - sAngel) > Math.PI ? "1" : "0";//大于Math.PI需要畫(huà)大弧,否則畫(huà)小弧
    drawData.push(itemData);//保存到drawData數(shù)組中,繪制時(shí)遍歷
    sAngel = eAngel;//將下一項(xiàng)數(shù)據(jù)的起始弧度設(shè)置為當(dāng)前項(xiàng)的結(jié)束弧度
}

下面,遍歷drawData,繪制環(huán)圖:

//遍歷計(jì)算每項(xiàng)數(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", //畫(huà)筆大小為零
        /**
         * d屬性設(shè)置路徑字符串
         * M ${P[0][0]} ${P[0][1]} 移動(dòng)畫(huà)筆到P0點(diǎn)
         * L ${P[1][0]} ${P[1][1]} 繪制一條直線(xiàn)到P1
         * A ${R} ${R} 0 ${v.LargeArcFlag} 1 ${P[2][0]} ${P[2][1]} 繪制外環(huán)弧到P2,R為外半徑,v.LargeArcFlag畫(huà)大弧還是小弧
         * L ${P[3][0]} ${P[3][1]} 繪制一條直線(xiàn)到P3
         * A ${r} ${r}  0 ${v.LargeArcFlag} 0 ${P[0][0]} ${P[0][1]} 繪制內(nèi)環(huán)弧到P0點(diǎn)
         * 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); //添加到畫(huà)布中
} 

到這里,就已經(jīng)繪制出如下環(huán)圖了:

記得還需要把相關(guān)變量先聲明一下。

、繪制線(xiàn)條、文字

下面我們需要繪制線(xiàn)條和文字。

繪制線(xiàn)條需要的數(shù)據(jù)

起點(diǎn)坐標(biāo):這里設(shè)置起點(diǎn)為每項(xiàng)數(shù)據(jù)的弧線(xiàn)中間位置,通過(guò)計(jì)算中間位置對(duì)應(yīng)的弧度,就可以通過(guò)三角函數(shù)計(jì)算出這個(gè)點(diǎn)坐標(biāo);

線(xiàn)束點(diǎn)坐標(biāo):當(dāng)線(xiàn)條起點(diǎn)在右側(cè)時(shí),線(xiàn)束點(diǎn)就是垂直平行起點(diǎn)圖表最右側(cè)位置;當(dāng)線(xiàn)條起點(diǎn)在左側(cè)時(shí),線(xiàn)束點(diǎn)就是垂直平行起點(diǎn)圖表最右左位置;假設(shè)起點(diǎn)為[sx,sy],右左結(jié)束點(diǎn)應(yīng)該就是[w,sy]、[0,sy],w為圖表寬度;

折點(diǎn):需要處理數(shù)據(jù)會(huì)擠在一起的情況,就會(huì)改變結(jié)束點(diǎn)坐標(biāo)的y值,當(dāng)起點(diǎn)和結(jié)束點(diǎn)y值不相等時(shí),就需要設(shè)置折點(diǎn)。

繪制文字:調(diào)整過(guò)后的線(xiàn)束點(diǎn),就是文字的位置。
以下是完整代碼:




    
    svg-pie


    

也可以查看gitee。

總結(jié)一下

難點(diǎn):難的地方就在弧上各點(diǎn)的計(jì)算,需要好好再回憶一下數(shù)學(xué)的三角函數(shù)。

對(duì)比canvas:canvas只需有一個(gè)標(biāo)簽,svg實(shí)現(xiàn)就在DOM中增加了一堆標(biāo)簽。這樣一來(lái),svg的優(yōu)勢(shì)就在于第項(xiàng)都是一個(gè)標(biāo)簽,你可以直接針對(duì)這個(gè)標(biāo)簽要綁定事件和做修改。比如要實(shí)現(xiàn)鼠標(biāo)稱(chēng)到某個(gè)項(xiàng),放大這個(gè)項(xiàng),svg只要給每個(gè)path綁定事件,修改當(dāng)前的這個(gè)path就行;而canvas只能在canvase綁定事件,先通過(guò)計(jì)算鼠標(biāo)位置來(lái)判斷移動(dòng)到了哪個(gè)份額上,然后再重繪整個(gè)canvas;同樣,標(biāo)簽過(guò)多也是svg的缺點(diǎn),我們這點(diǎn)標(biāo)簽其實(shí)沒(méi)什么,一旦標(biāo)簽多起來(lái),肯定是會(huì)給瀏覽器渲染帶來(lái)負(fù)擔(dān)的。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/94533.html

相關(guān)文章

  • Graph 數(shù)據(jù)可視化:JS 自動(dòng)布局有向無(wú)環(huán)圖

    摘要:可以用于模型化許多不同種類(lèi)的信息,因此將一個(gè)數(shù)據(jù)結(jié)構(gòu)可視化的需求也變得非常普遍。并且由于大部分圖的數(shù)據(jù)都非常復(fù)雜甚至動(dòng)態(tài)變化,所以自動(dòng)可配置的可視化布局算法顯然比人為排版更為高效且可靠。 有向無(wú)環(huán)圖(DAG)布局 有向無(wú)環(huán)圖及其布局算法 有向無(wú)環(huán)圖(directed acyclic graph,以下簡(jiǎn)稱(chēng) DAG)是一種常見(jiàn)的圖形,其具體定義為一種由有限個(gè)頂點(diǎn)和有限條帶有方向的邊組成的圖...

    zhouzhou 評(píng)論0 收藏0
  • 有向無(wú)環(huán)圖自動(dòng)布局

    摘要:判斷是否成環(huán)執(zhí)行拓?fù)渑判颍绻蛄兄械捻旤c(diǎn)數(shù)不等于有向圖的頂點(diǎn)個(gè)數(shù),則說(shuō)明圖中存在環(huán)。如果訪(fǎng)問(wèn)過(guò),且不是其父節(jié)點(diǎn),那么就構(gòu)成環(huán)圖有向無(wú)環(huán)圖的最小路徑覆蓋圖存儲(chǔ)鄰接矩陣圖的鄰接矩陣存儲(chǔ)方式是用兩個(gè)數(shù)組來(lái)表示圖。 何為有向無(wú)環(huán)圖? 1、首先它是一個(gè)圖,然后它是一個(gè)有向圖,其次這個(gè)有向圖的任意一個(gè)頂點(diǎn)出發(fā)都沒(méi)有回到這個(gè)頂點(diǎn)的路徑,是為有向無(wú)環(huán)圖2、DAG(Directed Acyclic G...

    shenhualong 評(píng)論0 收藏0
  • FireFox下Canvas使用圖像合成繪制SVG的Bug

    摘要:本文適合適合對(duì)繪制圖形學(xué)前端可視化感興趣的讀者閱讀。結(jié)論已經(jīng)明顯瀏覽器下,用下繪制繪制圖的時(shí)候,的設(shè)置將不生效。下面是一段用于測(cè)試的代碼,表示用源圖像的形狀去挖空目標(biāo)圖像。后續(xù)繪制用臨時(shí)的替代圖片。 本文適合適合對(duì)canvas繪制、圖形學(xué)、前端可視化感興趣的讀者閱讀。 楔子 所有的事情都會(huì)有一個(gè)起因。最近產(chǎn)品上需要做一個(gè)這樣的功能:給一些圖形進(jìn)行染色處理。想想這還不是順手拈來(lái)的事情,早...

    mudiyouyou 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<