最终效果如下:
没有动态效果,如果你想有动态效果,可以参考这个大佬的文章, 或者这个大佬的github
下面是代码
在组件a的render 方法中
//这里暂定height为300
<canvas width={height * 2} height={height} id="tes-dashbaords-pieChart-blockingAlarms"></canvas>
然后 在组件a的componentDidMount方法中
//在组件a的componentDidMount方法中执行下面这行
this.drawPieChartOfBlockingAlarms(pieChartOfBlockingAlarms);//pieChartOfBlockingAlarms参数就是后台返回的数据
// 这是他的类型 pieChartOfBlockingAlarms?: IPieChartOfBlockingAlarms;
//我们来看看IPieChartOfBlockingAlarms 你可以单独建个文件放这个interface
export interface IPieChartOfBlockingAlarms {
downstreamJam?: number;
indexorStop?: number;
blackImage?: number;
emergencyStop?: number;
temperature?: number;
rejectControlError?: number;
}
然后这是drawPieChartOfBlockingAlarms方法
/**
* draw pie chart of blocking alarms
* @param pieChartOfBlockingAlarms? `IPieChartOfBlockingAlarms`pie chart data which comes from backend
*/
private drawPieChartOfBlockingAlarms(pieChartOfBlockingAlarms: IPieChartOfBlockingAlarms) {
const dataArr: number[] = [];
const textArr: string[] = [];
for (const key in pieChartOfBlockingAlarms) {
/** get each part value of pie chart then add it in dataArr */
dataArr.push(pieChartOfBlockingAlarms[key]);
textArr.push(key);
}
//每个部分对应的颜色
const colorArr = ["#2980b9", "#16a085", "#9bbb59", "#f39c12", "#c0392b", "#4b2c50"];
const canvasElement: HTMLCanvasElement = document.getElementById(
"tes-dashbaords-pieChart-blockingAlarms"
) as HTMLCanvasElement;
const ctx: CanvasRenderingContext2D = canvasElement.getContext("2d") as CanvasRenderingContext2D;
//半径
const radius = canvasElement.height / 3;
drawPartOfPieChart(ctx, radius, dataArr, colorArr, textArr);
}
}
然后这是drawPartOfPieChart方法
/** draw pie chart */
export const drawPartOfPieChart = (
ctx: CanvasRenderingContext2D,
radius: number,
dataArr: number[],
colorArr: string[],
textArr: string[]
) => {
//format data, use %
let totalNumber = 0;
const newDataArr: number[] = [];
for (let i = 0; i < dataArr.length; i++) {
totalNumber += dataArr[i];
}
for (let i = 0; i < dataArr.length; i++) {
newDataArr.push(dataArr[i] / totalNumber);
}
// start degree
let startAngle = 0;
// end degree
let endAngle = 0;
// circle center
const ox = radius * 1.8;
const oy = radius * 1.5;
for (let i = 0; i < dataArr.length; i++) {
// moved angle
const angle = newDataArr[i] * Math.PI * 2;
endAngle = endAngle + angle;
ctx.beginPath();
//move to circle center
ctx.moveTo(ox, oy);
// draw each part
ctx.arc(ox, oy, radius, startAngle, endAngle, false);
ctx.closePath();
ctx.fillStyle = colorArr[i];
ctx.fill();
// change start angle
startAngle = endAngle;
if (i === dataArr.length - 1) {
startAngle = 0;
endAngle = 0;
}
}
// 图例
const legendLength = 10;
const posX = radius * 3.5;
for (let j = 0; j < dataArr.length; j++) {
const number = 30 * (j + 2);
ctx.fillStyle = colorArr[j];
// icon
ctx.fillRect(posX, number, legendLength, legendLength);
ctx.moveTo(posX, number);
ctx.font = "bold 16px 'Roboto', Serif, Sans-serif, cursive, fantasy, Monospace";
const words = `${textArr[j]}`;
ctx.fillStyle = "#bac4c5";
// words
ctx.fillText(words, posX + legendLength + 5, number + legendLength + 1);
}
//折线和文字
let lineStartAngle = 0;
// broken line length
const brokenLine = 10;
// text length
const textLength = brokenLine * 4;
for (let i = 0; i < dataArr.length; i++) {
// moved angle
const angle = newDataArr[i] * Math.PI * 2;
endAngle = endAngle + angle;
// brokenLine
const newAngle = angle / 2;
const brokenAngle = lineStartAngle + newAngle;
lineStartAngle += angle;
// line
const distancex = Math.cos(brokenAngle);
const distancey = Math.sin(brokenAngle);
const startx = radius * distancex;
const starty = radius * distancey;
const endx = (radius + brokenLine) * distancex;
const endy = (radius + brokenLine) * distancey;
// intersection of start point of broken line and the circle
const beginx = ox + startx;
const beginy = oy + starty;
// intersection of end point of broken line and the circle
const overx = ox + endx;
const overy = oy + endy;
ctx.beginPath();
ctx.moveTo(beginx, beginy);
ctx.lineTo(overx, overy);
const percent = `${Math.round(100 * newDataArr[i])}%`;
let overlinex = overx + textLength;
let percentX = overx + 15;
if (overx <= ox) {
overlinex = overx - textLength;
percentX = overlinex;
}
ctx.lineTo(overlinex, overy);
ctx.strokeStyle = colorArr[i];
ctx.stroke();
ctx.font = "500 16px 'Roboto', Serif, Sans-serif, cursive, fantasy, Monospace";
ctx.fillStyle = colorArr[i];
ctx.fillText(percent, percentX, overy - 2);
}
};
应该就能画出来了