ECharts 实现环形图

931 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第5天,点击查看活动详情

ECharts 环形图

环形图.gif

话不多说先上图。原本想通过极坐标柱状图的实现方式来实现上图效果,但结果不尽人意。下方提供两种展现形式,第一种是在Made A Pie 中展现,方便查看修改源代码,另一种方法是把项目下载下来在本地运行,适合项目本身就是vue搭建的同学。

方式一:Made A Pie 中展现

madeapie.com/#/chartInfo…

方式二:Vue3 项目中展现

Vue3 项目搭建参考地址:juejin.cn/post/716978…

完整项目地址(此文章所使用源码均出自该项目):gitee.com/zhaodapeng/…

1.实现思路

图中所有元素都是通过ECharts实现,并未使用贴图。外圈刻度效果使用的是仪表盘的刻度实现,环形背景线和进度条是通过拼接环饼图实现,类型名称则使用固定Y轴位置的方式实现。

2.组件封装
# components/echarts/EchartsComp.vue

<template>
  <div id="echartsComp" :style="{ height: height, width: width }"></div>
</template>

<script>
import { getCurrentInstance } from "vue";

export default {
  name: "EchartsComp",
  props: {
    options: {
      type: Object,
      required: true,
    },
    width: {
      type: String,
      default: "200px",
    },
    height: {
      type: String,
      default: "200px",
    },
  },
  mounted() {
    // 渲染图表
    const { proxy } = getCurrentInstance();
    this.chart = proxy.$echarts.init(document.getElementById("echartsComp"));
    this.chart.setOption(this.options);

    // 事件
    this.chart.on("click", function (params) {
      console.log(params);
    });
  },
  methods: {
    Refresh() {
      console.log(this.options);
      this.chart.setOption(this.options);
    },
  },
};
</script>

3.代码实现

将数据和逻辑抽离,通过数据来生成配置项。目前是四组数据,如需增加数据组数,需要对类型名称的位置另做调整。

# views/echarts/RingDiagram.vue

<template>
  <div class="contBody">
    <h2 class="title" @click="handleTab">环形图(点击更换数据)</h2>
    <!-- echarts 环形图 -->
    <EchartsComp
      class="ringBar"
      ref="chart"
      :options="ringDiagramOptions"
      width="100%"
      height="600px"
    />
  </div>
</template>

<script>
import EchartsComp from "@/components/echarts/EchartsComp.vue";
export default {
  name: "RingDiagramPage",
  data() {
    return {
      ringDiagramOptions: {},
      ringDiagramData: [
        {
          name: "本科",
          value: 300,
        },
        {
          name: "硕士",
          value: 350,
        },
        {
          name: "大专",
          value: 400,
        },
        {
          name: "博士",
          value: 150,
        },
      ],
    };
  },
  components: { EchartsComp },
  created() {
    this.init();
  },
  methods: {
    init() {
      // 最大值
      this.sumValue = 500;
      const optionData = this.getData(this.ringDiagramData);

      this.ringDiagramOptions = {
        backgroundColor: "RGB(8,20,67)",
        color: [
          {
            type: "linear",
            x: 0,
            y: 0,
            x2: 1,
            y2: 1,
            colorStops: [
              {
                offset: 0,
                color: "rgba(10,31,95,1)",
              },
              {
                offset: 1,
                color: "rgba(1,232,254,1)",
              },
            ],
            global: false,
          },
        ],
        grid: {
          top: "16%",
          bottom: "54%",
          left: "50%",
          containLabel: false,
        },
        yAxis: [
          {
            type: "category",
            inverse: true,
            z: 3,
            axisLine: {
              show: false,
            },
            axisTick: {
              show: false,
            },
            axisLabel: {
              interval: 0,
              inside: false,
              textStyle: {
                color: "RGB(78,184,252)",
                fontSize: 20,
              },
              show: true,
            },
            data: optionData.yAxis,
          },
        ],
        xAxis: [
          {
            show: false,
          },
        ],
        series: optionData.series,
      };
    },
    getData(data) {
      let res = {
        series: [
          {
            name: "大环",
            type: "gauge",
            splitNumber: 15,
            radius: "92%",
            center: ["50%", "55%"],
            startAngle: 90,
            endAngle: -270,
            axisLine: {
              show: false,
              lineStyle: {
                color: [[1, "#1f59a7"]],
              },
            },
            axisTick: {
              show: false,
            },
            splitLine: {
              show: true,
              length: 32,
              lineStyle: {
                color: "auto",
                width: 3.5,
              },
            },
            axisLabel: {
              show: false,
            },
            detail: {
              show: false,
            },
          },
          {
            name: "小环",
            type: "gauge",
            splitNumber: 15,
            radius: "88%",
            center: ["50%", "55%"],
            startAngle: 90,
            endAngle: -269.9999,
            axisLine: {
              show: false,
            },
            axisTick: {
              show: true,
              lineStyle: {
                color: "#1f59a7",
                width: 3,
              },
              length: 20,
              splitNumber: 5,
            },
            splitLine: {
              show: false,
            },
            axisLabel: {
              show: false,
            },
            detail: {
              show: false,
            },
          },
        ],
        yAxis: [],
      };
      for (let i = 0; i < data.length; i++) {
        res.series.push({
          name: "学历",
          type: "pie",
          clockWise: true,
          z: 2,
          hoverAnimation: false,
          radius: [73 - i * 15 + "%", 68 - i * 15 + "%"],
          center: ["50%", "55%"],
          label: {
            show: true,
            formatter: "{d}%",
            color: "RGB(246,175,101)",
            fontSize: 20,
            position: "inside",
          },
          labelLine: {
            show: false,
          },
          data: [
            {
              value: data[i].value,
              name: data[i].name,
            },
            {
              value: this.sumValue - data[i].value,
              name: "",
              itemStyle: {
                color: "rgba(0,0,0,0)",
                borderWidth: 0,
              },
              tooltip: {
                show: false,
              },
              label: {
                show: false,
              },
              hoverAnimation: false,
            },
          ],
        });
        res.series.push({
          name: "背景线",
          type: "pie",
          silent: true,
          z: 1,
          clockWise: true,
          hoverAnimation: false,
          radius: [71 - i * 15 + "%", 69 - i * 15 + "%"],
          center: ["50%", "55%"],
          label: {
            show: false,
          },
          itemStyle: {
            label: {
              show: false,
            },
            labelLine: {
              show: false,
            },
            borderWidth: 5,
          },
          data: [
            {
              value: 100,
              itemStyle: {
                color: "RGB(12,64,128)",
                borderWidth: 0,
              },
              tooltip: {
                show: false,
              },
              hoverAnimation: false,
            },
          ],
        });
        res.yAxis.push(data[i].name);
      }
      return res;
    },
    async handleTab() {
      // 数据更新
      this.ringDiagramData = [
        {
          name: "本科",
          value: this.getNum(350, 400),
        },
        {
          name: "硕士",
          value: this.getNum(300, 350),
        },
        {
          name: "大专",
          value: this.getNum(250, 300),
        },
        {
          name: "博士",
          value: this.getNum(100, 150),
        },
      ];
      await this.init();
      this.$refs.chart.Refresh();
    },
    // 生成随机数
    getNum(min, max) {
      const num = parseInt(Math.random() * (max - min + 1) + min);
      return Math.floor(num);
    },
  },
};
</script>

<style scoped>
.contBody {
  width: 100%;
  box-sizing: border-box;
}

.contBody .title {
  padding: 0 0 24px;
}
</style>