vue3+ts+svg实现echarts graph图label上显示进度条

最近接到一个需求,要求在echarts label上显示一个进度条,这玩意首先想到的是富文本+图片,但是让ui切图的话又太蠢了点,而且从0%到100%以精度0.1来算的话得让ui画1001个图,实在不怎么优雅,然后想能不能前端自己画图呢,进度条用svg应该很好实现,只要把svg转base64就能在echarts中使用了

关键代码:svg转base64

const svgDom: HTMLElement = svg.value as HTMLElement;
const s: string = new XMLSerializer().serializeToString(svgDom);
const encodedData: string = BASE_CODE + window.btoa(s);

直接上代码,echarts容器:

<template>
  <div class="main_page">
    <div id="chart" ref="chartBox"></div>
    <div v-for="item in svgList" :key="item" v-show="false">
      <svg-process :rate="item" @emitBase64="emitBase64"></svg-process>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { SvgType } from '@/types/svgInterface';
import { onMounted, ref } from 'vue';
import { EChartsOption, init } from 'echarts';
import SvgProcess from '@/components/SvgProcess.vue';
const svgArr: Array<number> = [];
const axisData = ['Mon', 'Tue', 'Wed', 'Very Loooong Thu', 'Fri', 'Sat', 'Sun'];
const base64Map = new Map();
const svgList = ref([]);
const chartBox = ref();
const emitBase64 = (item: SvgType) => {
  base64Map.set(item.objKey, item.value);
};
const data = axisData.map(function (_, i) {
  const rate:number = Math.round(Math.random() * 1000) / 10;
  svgArr.push(rate);
  const value = Math.round(Math.random() * 1000 * (i + 1));
  return {
    value,
    rate,
    label: {}
  };
});
(svgList.value as Array<number>) = [...new Set(svgArr)];
const links = data.map(function (_, i) {
  return {
    source: i,
    target: i + 1
  };
});
const getBase64ByKey = (key: number) => {
  return new Promise((resolve) => {
    const timer = setInterval(() => {
      if (base64Map.has(key)) {
        resolve(base64Map.get(key));
        clearInterval(timer);
      }
    }, 50);
  });
};
links.pop();
const initChart = async () => {
  const chartDom: HTMLElement = chartBox.value as HTMLElement;
  const chart = init(chartDom);
  for (let i = 0, len = data.length; i < len; i++) {
    const rate = data[i].rate;
    let base64 = '';
    if (base64Map.has(rate)) {
      base64 = base64Map.get(rate);
    } else {
      await getBase64ByKey(rate);
      base64 = base64Map.get(rate);
    }
    data[i].label = {
      show: true,
      formatter: (args: { value: number; }) => {
        return `{text|${args.value}}\n{bar|}`;
      },
      position: [2, 0],
      rich: {
        bar: {
          backgroundColor: {
            image: base64
          }
        },
        text: {
          padding: [0, 0, 0, 5],
          align: 'left',
          width: 30,
          height: 40
        }
      }
    };
  }
  svgList.value = [];
  const option: EChartsOption = {
    title: {
      text: '以进度条作为label显示'
    },
    tooltip: {},
    xAxis: {
      type: 'category',
      boundaryGap: false,
      data: axisData
    },
    yAxis: {
      type: 'value'
    },
    series: [
      {
        type: 'graph',
        layout: 'none',
        coordinateSystem: 'cartesian2d',
        symbolSize: 40,
        edgeSymbol: ['circle', 'arrow'],
        edgeSymbolSize: [4, 10],
        data,
        links,
        lineStyle: {
          color: '#2f4554'
        }
      }
    ]
  };
  option && chart.setOption(option);
};
onMounted(() => {
  initChart();
});
</script>
<style lang="less" scoped>
.main_page {
  width: 100vw;
  height: 100vh;
  #chart {
    width: 100%;
    height: 100%;
  }
}
</style>

svg转base64的组件SvgProcess

<template>
  <svg width="120" height="20" viewBox="0 0 120 20" ref="svg">
    <path
      d="M 5 10 L 55 10"
      stroke="#ddd"
      stroke-width="8"
      stroke-linecap="round">
  </path>
  <path
    class="progress"
    d="M 5 10 L 55 10"
    stroke="rgb(64, 158, 255)"
    stroke-dasharray="50px"
    :stroke-dashoffset="svgRate"
    stroke-width="8"
    stroke-linecap="round">
  </path>
  <text x="70" y="15" fill="#333" font-size="14">{{ props.rate }}%</text>
</svg>
</template>
<script setup lang="ts">
import { SvgType } from '@/types/svgInterface';
import { onMounted, ref, defineProps, computed } from 'vue';
const emit = defineEmits(['emitBase64']);
const BASE_CODE: string = 'data:image/svg+xml;base64,';
const props = defineProps({
  rate: {
    type: Number,
    default: 0
  }
});
const svg = ref();
const svgRate = computed(() => {
  return 50 - props.rate / 2 + 'px';
});
// 初始化时获取svg元素,然后把svg转成base64,再把数据传出去
onMounted(() => {
  const svgDom: HTMLElement = svg.value as HTMLElement;
  const s: string = new XMLSerializer().serializeToString(svgDom);
  const encodedData: string = BASE_CODE + window.btoa(s);
  const params: SvgType = {
    objKey: props.rate,
    value: encodedData
  };
  emit('emitBase64', params);
});
</script>

效果图

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值