在我们的大屏可视化项目中,经常会用到各种各样的图表。与传统的表格展示、枯燥的文字阐述相比,图表展示则使用户看起来更加直观、数据的展示则更加一目了然。本文基于svg绘图技术结合前端技术栈vue,以工作中常用的环形进度条为例,简单阐述一下绘制进度条的一些思路,希望能和大家共勉
思路讲解:绘制圆环,选型技术有多样,比如可以使用canvas绘制、使用div模拟,使用echarts插件等等,这里以svg技术为例。说起绘制圆环,我们可以使用svg中的circle标签,当然也可以使用path标签,一个圆环作为背景,另一个圆环作为进度环。这里就有一个疑问,既然都是圆,怎么根据实际的数值绘制对应的弧度呢?这里就需要用到一个高级的css属性,stroke-dashoffset-具体用法可以查看相关api文档。在大多数的业务场景中,环形进度条总是奇形怪状,例如有的环形进度条像是带有刻度的,一段一段的,这就需要用到另一个高级css属性了,stroke-dasharray。使用stroke-dashoffset和stroke-dasharray,结合svg中的渐变和旋转等知识点,我们可以绘制出各种各样的进度条,如下图:
基于以上思路,首先我们来封装一个进度条组件,代码如下
<template>
<div class="progress">
<svg
xmlns="http://www.w3.org/2000/svg"
width="100%"
height="100%"
viewBox="0 0 400 400"
>
<!-- 底部背景圆环 -->
<circle
cx="200"
cy="200"
:r="radius"
:stroke="bgRingColor"
:stroke-width="strokeWidth"
fill="none"
/>
<!-- 进度条圆环 -->
<circle
class="progress-bar"
ref="progressbar"
cx="200"
cy="200"
:r="radius"
:stroke="ringColor"
:stroke-width="strokeWidth"
fill="none"
:stroke-linecap="strokeLinecap"
transform="rotate(-90, 200, 200)"
:stroke-dasharray="strokeDasharray"
:stroke-dashoffset="strokeDashoffset"
/>
</svg>
<!-- 中间描述文字 -->
<div class="progress-desc">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: "Progress",
props: {
rate: {
type: Number,
default: 0,
},
bgRingColor: {
type: String,
default: "#ebeef5",
},
ringColor: {
type: String,
default: "#67C23A",
},
strokeLinecap: {
type: String,
default: "round",
},
strokeWidth: {
type: Number,
default: 20,
},
},
computed: {
radius() {
return 200 - this.strokeWidth / 2;
},
strokeDasharray() {
return 2 * Math.PI * this.radius;
},
strokeDashoffset() {
let val = 0;
let rate = this.rate;
if (rate > 1) {
rate = 1;
} else if (rate < 0) {
rate = 0;
}
if (this.strokeLinecap === "round") {
val = 2 * Math.PI * this.radius * (1 - rate) + this.strokeWidth;
} else {
val = 2 * Math.PI * this.radius * (1 - rate);
}
return val;
},
},
};
</script>
<style scoped>
.progress {
position: relative;
width: 100%;
height: 100%;
}
.progress-desc {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
font-family: "Microsoft YaHei";
font-size: 20px;
text-align: center;
}
.progress-bar {
transition: stroke-dashoffset 1s ease;
}
</style>
到这里,我们封装了进度条的基本属性及一些必传的参数,我们就可以在在相应的组件中使用这个进度条组件了,通过配置不同的样式,就可以完成各种各样的小案例了
<template>
<div class="progress">
<div class="svg-module">
<div class="module-title">条纹进度条一</div>
<div class="module-content">
<svg-line
:rate="0.3456"
bg-ring-color="#25b489"
ring-color="#ec6841"
:stroke-width="20"
stroke-linecap="butt"
:part="20"
:part-gap="12"
>
</svg-line>
</div>
</div>
<div class="svg-module">
<div class="module-title">条纹进度条二</div>
<div class="module-content">
<svg-line
:rate="0.8825"
bg-ring-color="#446224"
ring-color="#a3fe49"
:stroke-width="20"
stroke-linecap="butt"
:part="20"
:part-gap="12"
>
<span>
<span>{{ `${(0.8825 * 100).toFixed(2)}%` }}</span>
</span>
</svg-line>
</div>
</div>
<div class="svg-module">
<div class="module-title">条纹进度条三</div>
<div class="module-content">
<svg-line
:rate="0.8888"
bg-ring-color="#13387e"
ring-color="#1cbd9c"
:stroke-width="30"
stroke-linecap="butt"
:part="40"
:part-gap="8"
:show-gradient="true"
:gradient="myGradient"
>
<span>
<span>渐变进度条</span><br />
<span>{{ `${(0.8888 * 100).toFixed(2)}%` }}</span>
</span>
</svg-line>
</div>
</div>
<div class="svg-module">
<div class="module-title">条纹进度条四</div>
<div class="module-content">
<svg-line
:rate="0.5035"
bg-ring-color="#13387e"
ring-color="#00bbff"
stroke-linecap="butt"
:stroke-width="20"
:part="50"
:part-gap="20"
>
<span>
<span>{{ `${(0.5035 * 100).toFixed(2)}%` }}</span>
<br />
<span>完成率</span>
</span>
</svg-line>
</div>
</div>
<div class="svg-module">
<div class="module-title">条纹进度条五</div>
<div class="module-content">
<svg-line
:rate="0.7667"
bg-ring-color="#13387e"
ring-color="orange"
:stroke-width="26"
stroke-linecap="butt"
:part="1"
:part-gap="0"
>
<span>
<span>普通进度条</span><br />
<span>{{ `${(0.7667 * 100).toFixed(2)}%` }}</span>
</span>
</svg-line>
</div>
</div>
<div class="svg-module">
<div class="module-title">条纹进度条六</div>
<div class="module-content">
<svg-line
:rate="0.7685"
bg-ring-color="#13387e"
ring-color="#00bbff"
:stroke-width="20"
:part="1"
:part-gap="0"
>
<span>
<span>带弧度进度条</span><br />
<span>{{ `${(0.7685 * 100).toFixed(2)}%` }}</span>
</span>
</svg-line>
</div>
</div>
<div class="svg-module">
<div class="module-title">条纹进度条七</div>
<div class="module-content">
<svg-line
:rate="0.3333"
bg-ring-color="#13387e"
ring-color="#00bbff"
:stroke-width="20"
:part="1"
:part-gap="0"
:show-gradient="true"
>
<span>
<span>渐变进度条</span><br />
<span>{{ `${(0.3333 * 100).toFixed(2)}%` }}</span>
</span>
</svg-line>
</div>
</div>
<div class="svg-module">
<div class="module-title">条纹进度条八</div>
<div class="module-content">
<svg-line
:rate="0.5632"
bg-ring-color="#ee6666"
ring-color="#fac858"
:stroke-width="200"
stroke-linecap="butt"
:part="1"
:part-gap="0"
>
</svg-line>
</div>
</div>
</div>
</template>
<script>
import SvgLine from "./svgline.vue";
export default {
name: "SVGLineIndex",
components: {
SvgLine,
},
data() {
return {
myGradient: {
// 同一个页面中id务必唯一
id: "svg-linear-gradient001",
x1: "100%",
y1: "100%",
x2: "0%",
y2: "0%",
colorStops: [
{
offset: "0%",
color: "#0ae787",
},
{
offset: "100%",
color: "#fe653c",
},
],
},
};
},
};
</script>
<style scoped>
.progress {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
align-content: space-around;
width: 100%;
height: 100%;
background-color: #071727;
overflow: hidden;
}
.svg-module {
width: 23%;
height: 46%;
}
.module-content {
width: 100%;
height: calc(100% - 30px);
padding: 10px;
border: 1px solid #064774;
color: #fff;
box-sizing: border-box;
}
.module-title {
position: relative;
z-index: 1;
width: 100%;
height: 30px;
line-height: 30px;
font-size: 16px;
text-align: center;
color: #fff;
}
.module-title::before,
.module-title::after {
content: "";
position: absolute;
z-index: -1;
top: 0;
width: 50%;
height: 100%;
background-image: linear-gradient(to bottom, #061223, #042c4c);
}
.module-title::before {
left: 0;
transform: skew(-45deg);
transform-origin: left bottom;
}
.module-title::after {
right: 0;
transform: skew(45deg);
transform-origin: right bottom;
}
</style>