echarts 环形进度条限制最大长度为四分之三

echarts yekong

echarts项目开发中,需要实现多个环形进度条的效果,类似下图,环形进度条只会渲染四分之三的区域最后的四分之一的区域显示label描述,但是在实际渲染的时候,会遇到当其中一个数字太大占比超过75的时候,进度条会超出75%我们需要限制其最大不能超出75%,且lable正常显示。

echarts 环形进度条限制最大长度为四分之三

实例代码

<template>
  <div class="echarts1" ref="echarts1">

  </div>
</template>

<script>

import * as echarts from "echarts"

export default {
  name: 'echarts1',
  components: {},
  data() {
    return {}
  },
  props: {
    list: {
      type: Array,
      default() {
        return [];
      }
    },
    name: {
      type: String,
      default() {
        return '';
      }
    },
  },
  watch: {
    list() {
      this.drawEcharts()
    },
  },
  mounted() {
    var that = this;
    const viewElem = document.body;
    // 监听窗口变化,重绘echarts
    const resizeObserver = new ResizeObserver(() => {
      setTimeout(() => {
        that.drawEcharts();
      }, 300)
    });
    resizeObserver.observe(viewElem);
  },
  methods: {
    drawEcharts() {
      var that = this;
      let myChart = echarts.init(this.$refs.echarts1)
      let maxRadius = 80,
          barWidth = 5,
          barGap = 10;
      var arrValue = getArrayValue(this.list, "value");
      var sumValue = eval(arrValue.join('+'));
      var optionData = getData(this.list);

      function getArrayValue(array, key) {
        var key = key || "value";
        var res = [];
        if (array) {
          array.forEach(function (t) {
            res.push(t[key]);
          });
        }
        return res;
      }
      function getData(data) {
        var res = {
          series: [],
          yAxis: []
        };
        var maxDisplayPercentage = 0.75; // 最大显示占比为75%

        data.forEach((item, index) => {
          var itemPercentage = item.value / sumValue; // 计算每个项目的实际占比
          // var displayPercentage = Math.min(itemPercentage, maxDisplayPercentage); // 计算显示占比,不超过75%

          var displayPercentage = itemPercentage * maxDisplayPercentage; // 实际数据乘以0.75

          // 添加灰色背景部分
          res.series.push({
            name: '',
            type: 'pie',
            clockWise: false,
            hoverAnimation: false,
            radius: [(maxRadius - index * (barGap + barWidth)) + '%', (maxRadius - (index + 1) * barWidth - index * barGap) + '%'],
            center: ["50%", "50%"],
            label: {
              show: false
            },
            itemStyle: {
              borderWidth: 5
            },
            data: [{
              value: 7.5,
              itemStyle: {
                color: '#032f4e',
              },
              tooltip: {
                show: false
              },
              hoverAnimation: false
            },
              {
                value: 2.5,
                itemStyle: {
                  color: "rgba(0,0,0,0)",
                  borderWidth: 0
                },
                tooltip: {
                  show: false
                },
                hoverAnimation: false
              }
            ]
          });

          res.series.push({
            name: '安置方法',
            type: 'pie',
            clockWise: false,
            hoverAnimation: false,
            radius: [(maxRadius - index * (barGap + barWidth)) + '%', (maxRadius - (index + 1) * barWidth - index * barGap) + '%'],
            center: ["50%", "50%"],
            label: {
              show: false
            },
            itemStyle: {
              label: {
                show: false,
              },
              labelLine: {
                show: false
              },
              borderWidth: 5,
            },
            data: [{
              value: displayPercentage * 100, // 使用限制后的显示占比
              name: item.name
            }, {
              value: (1 - displayPercentage) * 100, // 剩余部分
              name: '',
              itemStyle: {
                color: "rgba(0,0,0,0)",
                borderWidth: 0
              },
              tooltip: {
                show: false
              },
              hoverAnimation: false
            }]
          });

          // y轴数据使用实际数值占比
          res.yAxis.push((itemPercentage * 100).toFixed(0) + "%");
        });

        return res;
      }

      var option = {
        tooltip: {
          show: false,
          trigger: "item",
          formatter: "{a}<br>{b}:{c}({d}%)"
        },
        color: this.list.map(obj => obj.color1),
        grid: {
          top: '8%',
          bottom: '63%',
          left: "50%",
          containLabel: false
        },
        yAxis: [{
          type: 'category',
          inverse: true,
          axisLine: {
            show: false
          },
          axisTick: {
            show: false
          },
          axisLabel: {
            interval: 0,
            inside: true,
            textStyle: {
              color: "rgba(235, 255, 255, 1)",
              fontSize: 13,
            },
            show: true
          },
          data: optionData.yAxis
        }],
        xAxis: [{
          show: false
        }],
        series: optionData.series
      };
      myChart.clear()
      myChart.resize()
      myChart.setOption(option)
    },
  }
}
</script>

<style lang="scss" scoped>
.echarts1 {
  position: relative;
  width: 60%;
  height: calc(100% - 10px);
}
</style>

使用实例

<template>
  <div class="echartsBodys">
    <echarts1 name="安置方法" :total="total" :list="list" ref="echarts"></echarts1>
    <div class="list">
      <div class="listItem cur" @click="getChecked(index)" v-for="(item,index) in list" :key="index">
        <div class="listIteml">
          <div class="dot" :class="{disabled:!item.checked}"
               :style="'background: linear-gradient(80deg,'+item.color1+' 0%, '+item.color2+' 100%);'"></div>
          <span :class="{disabled:!item.checked}">{{ item.name }}</span>
        </div>
        <em class="num"
            :style="{ color: item.checked ? item.color1 : '' }"
            v-if="item.checked">{{
            item.value
          }}</em>
        <em class="num"
            :style="{ color: item.checked ? item.color1 : '' }"
            v-else>{{
            item.value
          }}</em>
      </div>
    </div>
  </div>
</template>

<script>
import echarts1 from "./components/echarts.vue";

export default {
  name: "title",
  data() {
    return {
      list: [
        {
          name: '货币',
          value: 1268,
          color1: 'rgba(0, 168, 255, 1)',
          color2: 'rgba(0, 168, 255, 1)',
          checked: true
        },
        {
          name: '调产',
          value: 5689,
          color1: 'rgba(0, 238, 255, 1)',
          color2: 'rgba(0, 238, 255, 1)',
          checked: true
        },
        {
          name: '组合',
          value: 128,
          color1: 'rgba(255, 219, 113, 1)',
          color2: 'rgba(255, 219, 113, 1)',
          checked: true
        },
        {
          name: '迁建',
          value: 58,
          color1: 'rgba(255, 158, 116, 1)',
          color2: 'rgba(255, 158, 116, 1)',
          checked: true
        }]
    }
  },
  computed: {
    total: function () {
      var total = 0
      this.list.forEach((type) => {
        total += type.value
      });
      return total
    }
  },
  components: {echarts1},
  methods: {
    getChecked(index) {
      this.list[index].checked = !this.list[index].checked
      this.$refs.echarts.drawEcharts()
    }
  },
}
</script>

<style lang="scss" scoped>
.list {
  margin-left: 0px;
  position: relative;
  width: calc(40% - 0px);
  margin-right: 0px;
  height: calc(90% - 0px);
  margin-top: 0%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: nowrap;
  flex-direction: column;
  align-content: flex-start;

  .listItem {
    font-size: 14px;
    display: flex;
    height: 30%;
    width: calc(100% - 30px);
    margin: 0 auto;
    justify-content: flex-start;
    align-items: center;
    flex-wrap: nowrap;
    flex-direction: row;
    align-content: flex-start;

    .listIteml {
      display: flex;
      justify-content: flex-start;
      align-items: center;
      flex-wrap: nowrap;
      flex-direction: row;
      align-content: flex-start;
      font-size: 14px;
      font-family: MicrosoftYaHei;
      font-weight: 400;
      color: #FFFFFF;
      width: 100px;
    }

    span {
      font-size: 14px;
      font-family: PingFang SC-Regular, PingFang SC;
      font-weight: 400;
      color: rgba(235, 255, 255, 1);
    }

    .dot {
      width: 8px;
      height: 8px;
      margin-right: 10px;
      flex-shrink: 0;
      border-radius: 50%;
    }

    .dot.disabled {
      background: rgba(#999, 0.8) !important;
    }

    span.disabled {
      color: rgba(#999, 0.8) !important;
    }

    .num {
      font-size: 22px;
      font-family: DIN-Bold;
      font-weight: 500;
      color: #FFFFFF;
    }
  }
}

.echartsBodys {
  width: 100%;
  position: relative;
  height: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: nowrap;
  flex-direction: row;
  align-content: flex-start;
}

.echartsPage {
  width: 100%;
  position: relative;
  height: 100%;
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  flex-wrap: nowrap;
  flex-direction: column;
  align-content: flex-start;
}

.total {
  height: 50px;
  display: flex;
  justify-content: flex-start;
  align-items: center;
  flex-wrap: nowrap;
  flex-direction: row;
  align-content: flex-start;
  margin-left: 37px;
  font-size: 12px;
  font-family: MicrosoftYaHei;
  font-weight: 400;
  color: #FFFFFF;

  span {
    font-size: 26px;
    font-family: DIN-Bold;
    font-weight: 900;
    color: #FFFFFF;
    margin-left: 13px;
    text-shadow: 0 0 10px #2e99ec;
  }
}
</style>

更新

2024年01月31日

之前的效果是超过75%的都按照75%来算,但是客户对这一块追求比较精确,就算是95%也要能够看到95%的效果,所以这里我们改成实际进度乘以0.75来计算。

喜欢