渐变圆弧进度条有很多实现方式。dom(为了区分,这里所说的 dom 不包含 svg)、canvas、svg 都可以。鉴于 dom 实现比较麻烦,canvas 需要写 JavaScript,所以觉得还是 svg 方便一点。
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title></title>
<style>
.rotate-270 {
transform: rotate(270deg);
transform-origin: center;
}
.rotate-90 {
transform: rotate(90deg);
transform-origin: center;
}
</style>
</head>
<body>
<!-- 参考链接:https://www.jianshu.com/p/bfa223894867 -->
<!-- 渐变圆弧进度条 -->
<svg width="300" height="300">
<defs>
<linearGradient id="line-gradient-1">
<stop
offset="0%"
style="stop-color: purple;"
></stop>
<stop
offset="100%"
style="stop-color: pink;"
></stop>
</linearGradient>
</defs>
<circle
r="140"
cx="150"
cy="150"
fill="none"
stroke="url(#line-gradient-1)"
stroke-width="10"
stroke-linecap="round"
stroke-dasharray="700, 10000"
class="rotate-270"
></circle>
</svg>
<!-- 渐变圆弧进度条2 -->
<svg width="300" height="300">
<defs>
<linearGradient id="line-gradient-1">
<stop
offset="0%"
style="stop-color: purple;"
></stop>
<stop
offset="100%"
style="stop-color: pink;"
></stop>
</linearGradient>
<linearGradient id="line-gradient-2">
<stop
offset="0%"
style="stop-color: red;"
></stop>
<stop
offset="100%"
style="stop-color: purple;"
></stop>
</linearGradient>
</defs>
<circle
r="140"
cx="150"
cy="150"
fill="none"
stroke="url(#line-gradient-2)"
stroke-width="10"
stroke-linecap="round"
stroke-dasharray="360, 10000"
class="rotate-90"
></circle>
<circle
r="140"
cx="150"
cy="150"
fill="none"
stroke="url(#line-gradient-1)"
stroke-width="10"
stroke-linecap="round"
stroke-dasharray="440, 10000"
class="rotate-270"
></circle>
</svg>
</body>
</html>

stroke-dasharray 设置的是虚线的实虚部分。将虚线部分设置得很大,避免出现第二段实线,这样就将圆变成了圆弧。
因为渐变色是从右到左,圆弧的绘制也是如此。所以将圆弧旋转 270°,使其从顶部开始顺时针旋转。
左侧的那种当进度 100% 的时候是一个颜色自然过渡的圆。其实现只需要一个 circle 元素。
右侧的那种当进度 100% 的时候是一个颜色突变的圆。其实现需要两个 circle 元素。渐变也要定义两个。右侧的进度条分成左右两个部分。左半部分的圆弧是从 purple 到 red,右半部分的圆弧是从 pink 到 purple。当进度小于等于 50% 的时候,只显示右半部分。当进度大于 50% 的分布,左半部分也绘制。其实线长度为超出 50% 的部分。然后顺时针旋转 90° 就可以衔接右半部分了。
补充:
除了使用 circle 元素外,应该也可以使用 path 元素实现。
此外,还有一种比较麻烦的方法:用无数个填充的 circle 组成圆弧部分。就像串联起来的佛珠,只不过这些 circle 相对位移较小,看上去像是一个圆润的弧。至于每个的填充颜色,需要根据圆弧开始颜色和结束颜色计算。
ps:svg 还是很有用的,学好了可以在很多场景下代替 canvas 和 dom。
2022.03.04 补充:
之前没有记录自适应 svg。现在又有这种需求了,所以补充记录一下。
<template>
<svg viewBox="0 0 300 300">
<defs>
<linearGradient id="line-gradient-1">
<stop
offset="0%"
:style="`stop-color: ${startColor};`"
></stop>
<stop
offset="100%"
:style="`stop-color: ${endColor};`"
></stop>
</linearGradient>
</defs>
<circle
r="140"
cx="150"
cy="150"
fill="none"
:stroke="background"
stroke-linecap="round"
:stroke-width="strokeWidth"
stroke-dasharray="1000"
class="circle"
></circle>
<circle
r="140"
cx="150"
cy="150"
fill="none"
stroke="url(#line-gradient-1)"
stroke-linecap="round"
:stroke-width="strokeWidth"
:stroke-dasharray="`${strokeLength}, 10000`"
:style="`transform: rotate(${rotate}deg);`"
class="circle"
></circle>
</svg>
</template>
<script>
import { ref, toRefs } from 'vue'
export default {
name: 'SvgLinearRing',
props: {
// 圆环背景
background: {
type: String,
default: 'transparant'
},
// 渐变开始颜色
startColor: {
type: String,
default: 'purple'
},
// 渐变结束颜色
endColor: {
type: String,
default: 'pink'
},
// 线宽
strokeWidth: {
type: Number,
default: 10
},
// 进度
rate: {
type: Number,
default: 0
},
// 旋转角度
rotate: {
type: Number,
default: 90
}
},
setup(props) {
const { rate } = toRefs(props)
const strokeLength = ref(0)
strokeLength.value = rate.value * 280 * Math.PI
return {
strokeLength
}
}
}
</script>
<style lang="scss" scoped>
.circle {
transform-origin: center;
}
</style>
这里自适应的关键就是:viewBox=”0 0 300 300″。里面元素的数值都是相对于 300 而言的。300 是我随便取的的值,这里填什么看自己喜好,当然最好是方便计算百分比的。