vue中用echarts实现复合饼图,带关系连接线

1.拿到产品原型图,需求中有这样一个图表

2.翻看echart的饼图示例,没有这种复合饼图,只有一个嵌套饼图

3. 于是网上查网友的文章,查到两篇类似的贴子,(52条消息) echarts模仿excel复合饼图(饼-饼)_相忘于江湖426543的博客-CSDN博客_echarts复核饼图(52条消息) echarts实现复合饼图_JustMo_的博客-CSDN博客_echarts 复合饼图 其中一篇是react的,个别地方我看不太懂, 总之结合这两篇文章大概实现了这个需求

4. 首先是copy示例中的全部代码,在页面上先把这个嵌套的饼图搞出来

5. 移动中间的饼到右边,调整好两个饼的位置关系,这一步是通过media实现

在option中增加media属性,如图,内容见代码段

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

然后修改第一个饼的半径 radius,使它成为一个实心的饼

调整第一个饼的旋转角度 startAngle,让它的较小的一个角朝正右方,这样方便后面画两条连接线

计算方式:这一角的数据占总数的比例去乘以360再除以2

原理:一个饼图渲染的时候默认是从90°开始的,x轴正向是0°,y轴正向是90°,依此类推一圈回来是360°。

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

效果:

最后就剩下两条线了。画线用markLine属性,两点确定一条直线,所以要找到两条线的分卸的起点坐标和终点坐标,终点的话这里就好说了,直接找到右边小饼的上下两个点就ok了,重点是两个起点坐标。

这里选择的办法是:先找到圆心坐标 和半径长度,再利用数学公式cos 算出它的x坐标值。我这里的圆心横坐标是450,半径长是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);

全部代码:

<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>

最终效果图

猜你喜欢

转载自blog.csdn.net/zhuangjiajia09/article/details/128951572