1. 理想状态

ECharts 中的饼图也支持通过设置 roseType 显示成南丁格尔图。

roseType: 'angle'

南丁格尔图会通过半径表示数据的大小,下图是官方示例的效果图:
官方示例效果图

感觉棒棒哒,有没有?😜

2. 现实光景

在实际使用时,就会发现一些问题。比如下面这个:由于数据值之间的差距多大,而南丁格尔图会通过半径表示数据的大小,这就会造成极小值在图表中,半价会非常非常的小,如下图中的那一个“小红点”,十分尴尬:

图表的默认行为
在实际使用中,由于数据是从后台动态获取的,我们并不能保证数据的均匀性。因此,仅仅依靠 echarts 中关于南丁格尔图各个部分半径的默认设置,是不能完全满足合理展示数据这一基本需求的。

3. 放大数据

可以通过放大数据, 解决上面那个问题。

基本思路如下:

  1. 放大数据:选用一个合适的数字 additional 来放大要呈现的数据(如:最大的数据项的二分之一),原始数据项加上 additional ;
  2. 设置图表数据:将经过放大处理后的数据,用于设置图表的 series 的 data;
  3. 计算tooltip使用的数据: 如果图表的 tooltip 中涉及数据项的显示,还需要将数据项“还原”(数据项的值减去第一步增加的值)。

其中,需要特别注意:数据值为0的项,是不需要进行放大和还原的处理了。

理清思路后,实现起来就比较容易了,下面是经过放大处理后的图表效果图(最小值:1,最大值:1967):

最小值
极小值 1 的显示,没有像之前那么迷你了!

最大值

数值为0时的图表显示
关闭其它所有非 0 数据后,针对数据项 0 的显示也是正确的 – 没有出现负数的 tooltip 文字。

下面是这个图表组件的部分代码:

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

<script>
import { mapGetters } from "vuex";
import "./themes/classic.js";

let chart = null;

export default {
  name: "StockPieChart",
  props: {
    className: {
      type: String,
      default: "chart",
    },
    id: {
      type: String,
      default: "chart",
    },
    width: {
      type: String,
      default: "200px",
    },
    height: {
      type: String,
      default: "200px",
    },
  },

  data() {
    return {
      legendLabels: [...this.$store.getters.stockLegendLabels],
      data: [...this.$store.getters.stockData],
    };
  },

  computed: {
    ...mapGetters(["stockLegendLabels", "stockData"]),
  },
  watch: {
    stockLegendLabels(newValue) {
      if (newValue.length === 0) {
        this.legendLabels = [];
      }
      for (let i = 0; i < newValue.length; i++) {
        this.$set(this.legendLabels, i, newValue[i]);
      }
      this.$nextTick(() => {
        this.paint();
      });
    },
    stockData(newValue) {
      if (newValue.length === 0) {
        this.data = [];
      }
      for (let i = 0; i < newValue.length; i++) {
        this.$set(this.data, i, newValue[i]);
      }
      this.$nextTick(() => {
        this.paint();
      });
    },
  },

  mounted() {
    this.drawChart();
  },

  beforeDestroy() {
    if (!chart) {
      return;
    }

    // 清空 resize 事件处理函数
    window.removeEventListener("resize", this.resize);
    window.removeEventListener('beforeunload', this.clearChart);

    // 组件销毁前,回收图表
    chart.dispose();
    // chart.clear();
    chart = null;
  },
  methods: {
    drawChart() {
      // 1. 创建图表实例
      if (!chart) {
        chart = this.$echarts.init(
          document.getElementById(this.id),
          "classic",
          {
            renderer: "svg",
          }
        );
      }

      this.paint();

      window.addEventListener('beforeunload', this.clearChart);

      // 4.设置图表在页面尺寸变化时,图表重新渲染排布
      window.addEventListener("resize", this.resize);
    },
    // 绘制图表
    paint() {
      // 2. 设置图表基本配置
      this.initChart();

      // 3.获取图表数据并填入图表
      this.setChartData();
    },
    initChart() {
      chart.setOption({
        baseOption: {
          tooltip: {
            trigger: "item",
            position: "top",
            formatter: "{b}: {c} ({d}%)",
          },
          legend: {
            orient: "horizontal",
            top: 0,
            left: 30,
          },
          series: [
            {
              name: "库存状态",
              type: "pie",
              radius: ["40%", "70%"],
              center: ["50%", "56%"],
              avoidLabelOverlap: false,
              label: {
                show: false,
                position: "center",
              },
              emphasis: {
                label: {
                  show: true,
                  fontSize: "20",
                  fontWeight: "bold",
                },
              },
              labelLine: {
                show: false,
              },
            },
          ],
        },
        media: [
          {
            query: {
              minAspectRatio: 1.5,
              minWidth: 400,
            },
            option: {
              legend: {
                orient: "vertical",
              },
            },
          },
        ],
      });
    },
    setChartData() {
      chart.showLoading();
      chart.setOption({
        legend: {
          data: this.legendLabels,
        },
        series: [
          {
            data: this.data,
          },
        ],
      });
      chart.hideLoading();
    },
    resize() {
      chart.resize();
    },
    clearChart(){
      this.$destroy();
    }
  },
};
</script>

在其它组件中,引入并注册后使用:

<stock-pie-chart id="stock" width="30vw" height="60vh" />

🎉🎉🎉🌹🌹🌹

Logo

前往低代码交流专区

更多推荐