Use echarts in vue to implement a composite pie chart with relationship connecting lines

1. Get the product prototype diagram, there is such a diagram in the requirements

2. Look at echart’s pie chart examples. There is no such composite pie chart, only a nested pie chart.

3. So I checked netizens’ articles online and found two similar posts, (52 messages) echarts imitates excel compound pie chart (pie-pie)_Xiangwangyujianghu426543’s blog-CSDN blog_echarts review pie chart and (52 messages) echarts implements compound pie chart_JustMo_'s blog-CSDN blog_echarts compound pie chart. One of the articles is react, and I can't understand some parts. In short, the combination of these two articles probably achieves this requirement.

4. First, copy all the code in the example and create the nested pie chart on the page.

5. Move the middle pie to the right and adjust the positional relationship between the two pie. This step is achieved through media.

Add the media attribute to option, as shown in the figure, see the code snippet for content

media: [
          {
            query: { minAspectRatio: 1 },
            option: {
              series: [{ center: ["70%", "50%"] }, { center: ["30%", "50%"] }],
            },
          },
        ],

Then modify the radius of the first pie to make it a solid pie

Adjust the rotation angle startAngle of the first pie so that its smaller corner faces directly to the right. This will make it easier to draw two connecting lines later.

Calculation method: The proportion of the data in this corner to the total is multiplied by 360 and divided by 2

Principle: When a pie chart is rendered, it starts from 90° by default, the positive direction of the x-axis is 0°, the positive direction of the y-axis is 90°, and so on in one circle to 360°.

startAngle() {
      let sum = 0;
      this.dataArr.forEach((element) => {
        sum += element.value;
      });
      return 360 * (this.dataArr[0].value / sum) * 0.5;
    },

Effect:

In the end there were only two lines left. Use the markLine attribute to draw a line. Two points determine a straight line, so you need to find the starting point coordinates and the ending point coordinates of the two lines. The end point is easy to say here. Just find the upper and lower points of the right cake. The key point is are the two starting point coordinates.

The method chosen here is: first find the coordinates of the center of the circle and the length of the radius, and then use the mathematical formula cos to calculate its x coordinate value. The abscissa of the center of my circle here is 450, and the radius is 200.

 let x1 = x0 + (height / 4) * Math.cos((this.startAngle * 3.14) / 180);
      let y1 =
        height * 0.5 - (height / 4) * Math.sin((this.startAngle * 3.14) / 180);
 x2 = x1;
      y2 =
        height * 0.5 + (height / 4) * Math.sin((this.startAngle * 3.14) / 180);

All code:

<template>
  <div id="main" style="width: 1500px; height: 800px"></div>
</template>

<script>
import * as echarts from "echarts";
export default {
  data() {
    return {
      chartDom: null,
      myChart: null,
      chartView: null,
      dataArr: [
        { value: 220, name: "Baidu" },
        { value: 500, name: "Direct" },
      ],
    };
  },
  computed: {
    startAngle() {
      let sum = 0;
      this.dataArr.forEach((element) => {
        sum += element.value;
      });
      return 360 * (this.dataArr[0].value / sum) * 0.5;
    },
    option() {
      if (this.chartView) {
        console.log("222");
        // changePos = true;
        let tmp = this.chartView[1]._data._itemLayouts[0];
        // let tmp = this.chartView; //[1]._data._itemLayouts[0];
        pos.startTop = {
          x: tmp.cx + Math.cos(tmp.startAngle) * tmp.r,
          y: tmp.cy + Math.sin(tmp.startAngle) * tmp.r,
        };
        pos.startBootom = {
          x: tmp.cx + Math.cos(tmp.endAngle) * tmp.r,
          y: tmp.cy + Math.sin(tmp.endAngle) * tmp.r,
        };
      }

      return {
        tooltip: {
          trigger: "item",
          formatter: "{a} <br/>{b}: {c} ({d}%)",
        },
        legend: {
          data: [
            "Direct",
            "Marketing",
            "Search Engine",
            "Email",
            "Union Ads",
            "Video Ads",
            "Baidu",
            "Google",
            "Bing",
            "Others",
          ],
        },
        series: [
          {
            name: "Access From",
            type: "pie",
            // selectedMode: "single",
            radius: [0, "30%"],
            label: {
              position: "inner",
              fontSize: 14,
            },
            labelLine: {
              show: false,
            },
            data: [
              { value: 1548, name: "Search Engine" },
              { value: 775, name: "Direct" },
              { value: 679, name: "Marketing", selected: true },
            ],
          },
          {
            name: "Access From",
            type: "pie",
            radius: [0, "50%"],
            labelLine: {
              length: 30,
            },
            label: {
              formatter: "{a|{a}}{abg|}\n{hr|}\n  {b|{b}:}{c}  {per|{d}%}  ",
              backgroundColor: "#F6F8FC",
              borderColor: "#8C8D8E",
              borderWidth: 1,
              borderRadius: 4,
              rich: {
                a: {
                  color: "#6E7079",
                  lineHeight: 22,
                  align: "center",
                },
                hr: {
                  borderColor: "#8C8D8E",
                  width: "100%",
                  borderWidth: 1,
                  height: 0,
                },
                b: {
                  color: "#4C5058",
                  fontSize: 14,
                  fontWeight: "bold",
                  lineHeight: 33,
                },
                per: {
                  color: "#fff",
                  backgroundColor: "#4C5058",
                  padding: [3, 4],
                  borderRadius: 4,
                },
              },
            },
            data: this.dataArr,
            startAngle: this.startAngle,
            markLine: {
              silent: true,
              symbol: "none",
              data: this.getMarkLineData(),
            },
          },
        ],
        media: [
          {
            query: { minAspectRatio: 1 },
            option: {
              series: [{ center: ["70%", "50%"] }, { center: ["30%", "50%"] }],
            },
          },
        ],
      };
    },
  },
  mounted() {
    this.chartDom = document.getElementById("main");
    this.myChart = echarts.init(this.chartDom);

    this.myChart.setOption(this.option);
    console.log("mychart", this.myChart);
  },
  methods: {
    getMarkLineData(percent) {
      // 1.获取画布 width,height
      let height = this.myChart.getHeight();
      let width = this.myChart.getWidth();
      console.log("width", width, "height", height);

      // 2.  根据 series[0].center 获取圆心坐标
      let x0 = 450; // 圆心x轴坐标
      let x1 = x0 + (height / 4) * Math.cos((this.startAngle * 3.14) / 180);
      let y1 =
        height * 0.5 - (height / 4) * Math.sin((this.startAngle * 3.14) / 180);
      x2 = x1;
      y2 =
        height * 0.5 + (height / 4) * Math.sin((this.startAngle * 3.14) / 180);
      let result = [
        [
          {
            x: x1,
            y: y1,
          },
          {
            x: "70%",
            y: "35%",
          },
        ],
        [
          {
            x: x1,
            y: y2,
          },
          {
            x: "70%",
            y: 521,
          },
        ],
      ];
      return result;
    },
  },
};
</script>

final renderings

Guess you like

Origin blog.csdn.net/zhuangjiajia09/article/details/128951572
Recommended