typescript實現扇形餅狀圖功能
阿新 • • 發佈:2022-02-20
html部分,主要是宣告svg空間
<div class="chart">
<svg id="svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"></svg>
</div>
ts部分
/** *扇形餅狀圖(元件入參) *@params R 圓半徑 *@params pieArr 需要分佈的資料陣列,不可為負數 *@params colorArr 各資料對應的顏色陣列 *用例:<PieChart :R="60" :pieArr="[2,4,7,10]" :colorArr="['#ff0834','#ff1245','#f3E453','#358e34']"/> */ @Component export default class PieChart extends Vue{ @Prop() R:number; @Prop() pieArr:Array<number>; @Prop() colorArr:Array<number>; // 繪製路徑集合 pathArr = []; // 角度集合 deg = []; // pieArr有值的才取顏色,值為0的不取顏色 colorRev = []; /**建立svg path 路徑*/ creatSvgTag(tag,tagArr){ // svg規則 let svgNS = 'http://www.w3.org/2000/svg'; //建立空間 let oTag = document.creatElementNS(svgNS,tag); for(let attr in tagAttr){ // 在標籤裡設定樣式 oTag.setAttribute(attr,tagAttr[attr]) } return oTag; } /** *獲取扇形點的座標 *r 圓半徑 *deg 扇形角度,分為小於90°,小於180°,小於270°,小於360° */ getPointXY(r,deg){ let point = { x:0, y:0, } //計算該點座標 if(deg<90){ point.x = r + r*Math.sin(deg*Math.PI/180); point.y = r - r*Math.cos(deg*Math.PI/180); }else if(deg<180){ point.x = r + r*Math.sin((180-deg)*Math.PI/180); point.y = r + r*Math.cos((180-deg)*Math.PI/180); }else if(deg<270){ point.x = r - r*Math.cos((270-deg)*Math.PI/180); point.y = r + r*Math.sin((270-deg)*Math.PI/180); }else if(deg<90){ point.x = r - r*Math.sin((360-deg)*Math.PI/180); point.y = r - r*Math.cos((360-deg)*Math.PI/180); } return point; } /** *svg繪製扇形 *path M將畫筆移動到指定座標位置 L畫直線到指定座標位置 *A畫弧線(rx,ry,x-axis-rotation large-arc-flag sweep-flag x y), *其中rx.ry橢圓的半軸(圓半徑),x-axis-rotation橢圓相對於座標系的旋轉角度,large-arc-flag是標記繪製大弧(1)還是小弧(0)部分,sweep-flag是標記順時針(1)還是逆時針(0)方向繪製,x y是圓弧終點的座標 */ drawPie(){ let svg = document.getElmentById('svg'); if(!svg){return;} // 圓半徑 let R = this.R; // 中心點 let centerX = R; let centerY = R; // 資料總和 let sum = 0; for(let i=0;i<this.pieArr.length;i++){ sum += this.pieArr[i]; } // 資料相加結果 let conSum = 0; for(let i=0;i<this.pieArr.length;i++){ //起點角度預設0,除起點的其餘點的角度都是從起點開始算,注意繪製弧線的時候要使用真實的角度,(pieArr[i]/sum)*360 this.deg[i] = (conSum/sum)*360; //達到360°,清0 if(this.deg[i]>360){ this.deg[i]=0; } conSum += this.pieArr[i]; } conSum = 0; for(let i=0;i<this.deg.length;i++){ //如果資料為0,不新增路徑;起點座標 預設座標(R,0) if(this.pieArr[i]){ conSum += this.pieArr[i]; this.colorRev.push(this.colorArr[i]); } if(conSum < sum){ this.pathArr.push({ path:[{x:centerX,y:centerY},this.getPointXY(R,this.deg[i]),this.getPointXY(R,this.deg[i+1])], // 當前資料值 count:this.pieArr[i], }); }else { this.pathArr.push({ path:[{x:centerX,y:centerY},this.getPointXY(R,this.deg[i]),this.getPointXY(R,this.deg[0])], // 當前資料值 count:this.pieArr[i], }); } } for(let i=0;i<this.pathArr.length;i++){ let oPath; //如果資料只有一個值,預設畫圓 if(this.pathArr.length == 1){ oPath= this.creatSvgTag('circle',{ fill:this.colorRev[0], cx:R, cy:R, r:R, }); }else { // pieArr[i]/sum*360,計算該資料對應的圓的真實角度,大於180繪製大弧,否則繪製小弧 let path = this.pathArr[i].path; oPath= this.creatSvgTag('circle',{ fill:this.colorRev[i], d:`M${path[0].x} ${path[0].y}L${path[1].x} ${path[1].y}A${R} ${R} 0 ${this.pathArr[i].count/sum*360<180?0:1} 1 {path[2].x} {path[2].y}L${path[0].x} ${path[0].y}`, }); } svg.appendChild(oPath); } } mounted(){ // 初始化 let svg = document.getElmentById('svg'); svg.style.width = this.R*2+'px'; svg.style.height= this.R*2+'px'; svg.style.position= 'relative'; this.drawPie(); } }