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();
}
}