ts+react画canvas饼状图

 最终效果如下:

没有动态效果,如果你想有动态效果,可以参考这个大佬的文章, 或者这个大佬的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);
  }
};

应该就能画出来了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,似乎您想了解如何在Vue3中使用TypeScriptReact。Vue3和React是两个不同的框架,不能同时使用。如果您想在Vue3中使用TypeScript,可以按照以下步骤进行操作: 1.安装Vue CLI并创建一个新项目: ```shell npm install -g @vue/cli vue create my-project ``` 2.在创建的Vue项目中安装TypeScript: ```shell cd my-project vue add typescript ``` 3.现在您可以在Vue3项目中使用TypeScript了。例如,在Vue组件中使用TypeScript: ```typescript <template> <div>{{ message }}</div> </template> <script lang="ts"> import { defineComponent } from 'vue' export default defineComponent({ data() { return { message: 'Hello World!' } } }) </script> ``` 如果您想在Vue3中使用React,可以使用Vue3的Composition API来实现。以下是一个使用Vue3和React的示例: ```typescript <template> <div> <h1>{{ title }}</h1> <ReactComponent :message="message" /> </div> </template> <script lang="ts"> import { defineComponent, ref } from 'vue' import { createApp } from 'vue' import React from 'react' import ReactDOM from 'react-dom' const ReactComponent = defineComponent({ props: { message: { type: String, required: true } }, mounted() { const el = document.createElement('div') document.body.appendChild(el) ReactDOM.render(<div>{this.message}</div>, el) } }) export default defineComponent({ setup() { const title = ref('Vue3 + React') const message = ref('Hello World!') return { title, message } }, components: { ReactComponent } }) </script> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值