环形统计图的话,大家第一反应应该是使用Echarts来实现,Echarts配置丰富,示例众多,而且现在也支持按需引入,可以减少打包后的体积。但是如果需求是在卡片或者表格中,作为一个图标形式来展示的话,这种情况下,一个页面中,可能会展示很多个,使用Echarts的话,可能就不是那么好了。
1. SVG
SVG应该是大家很容易想到的实现方式,这是因为我们平常所用的组件库中,有一个组件:环形进度条,看起来很像我们想要的效果,它就是用SVG来实现的。Arco Design中的迷你进度条,就更像了:
如果项目有使用组件库的话,并且环形标识中只展示2项,只需要把环形进度条的样式稍微改下,就能满足要求;如果超过2项的话,我们就得仿照环形进度条实现一个组件了。
先F12看下环形进度条包含哪些元素、哪些属性:
从图中可以看出,只用到了circle元素。进度条是由一个表示背景的环,加一个表示进度的环,两个都是circle。
stroke-dasharray: 定义了用于绘制形状轮廓的虚线段和间隙的排列形式,此处是周长:2 * Math.PI * 6(r = 6)stroke-dashoffset: 定义了虚线与路径起点之间的偏移量,此处偏移80%(2*Math.PI*6*0.8),展示的就是20%
基于这种实现,需要展示几项,就用几个circle表示就可以了,只需要计算出每个circle的stroke-dashoffset就行了:
<template>
<svg view-box="0 0 16 16" class="circle-svg">
<circle
v-for="(offset, index) in offsetArr"
:key="index"
fill="none"
cx="8"
cy="8"
r="6"
stroke-width="4"
:stroke="COLORS[index % 4]"
:stroke-dasharray="length"
:stroke-dashoffset="offset"
></circle>
</svg>
</template>
<script setup lang="ts">
import { computed, PropType, ref } from 'vue'
const COLORS = ['#165dff', '#ff7d00', '#f53f3f', '#00b42a'].reverse()
defineOptions({ name: 'RingIcon' })
const props = defineProps({
percents: { type: Array as PropType<number[]>, required: true },
})
const percentArr = ref([0.3, 0.4, 0.2, 0.1])
const length = Math.round(2 * Math.PI * 6 * 10000) / 10000
let sum = 0
const offsetArr = computed(() => {
return props.percents
.map(item => {
sum += item
return Math.round(length * (1 - sum) * 10000) / 10000
})
.reverse()
})
</script>
<style lang="less" scoped>
.circle-svg {
transform: rotate(-90deg);
width: 1em;
height: 1em;
}
</style>
- 优点:矢量图形,清晰不失真
- 缺点:多项的话,看起来就没那么圆了,锯齿看起来也会严重些
2. CSS
线性渐变和径向渐变,大家应该都很熟悉,其实还有一种渐变:锥形渐变 conic-gradient()。
CSS 函数
conic-gradient()创建一个由渐变组成的图像,渐变的颜色围绕一个中心点旋转(而不是从中心辐射)进行过渡。锥形渐变的例子包括饼图和色轮。conic-gradient()函数的结果是<gradient>数据类型的对象,此对象是一种特殊的<image>数据类型。
可以实现饼图,那环形图自然也可以,中间区域加一个遮罩就可以了:
- 优点:非常简单,几行CSS就能搞定
- 缺点:锯齿比较严重,尤其环形比较小和某段环形角度比较小时
总结
SVG和CSS都可以实现饼图和环形图,根据需求选择合适的实现方式,你学废了吗?