svg實現一個環形進度條
設計初衷:本來想直接使用element的進度條組件的,但是好多屬性都沒有辦法控制。
UI設計的圖如下,需要控制未完成和已完成的顏色,端點的形狀改為普通的butt
所以使用svg實現了一個環形進度條組件
element組件
設計圖實現效果
可以交互的svg 環形進度條組件
<template><div class="circular-progress-container"><svg :width="size" :height="size" viewBox="0 0 100 100" class="progress-svg"><!-- 背景圓(未完成部分) --><circleclass="progress-background"cx="50"cy="50":r="radius"fill="none":stroke="backgroundColor":stroke-width="strokeWidth" /><!-- 前景圓(已完成部分) --><circleclass="progress-foreground"cx="50"cy="50":r="radius"fill="none":stroke="progressColor":stroke-width="strokeWidth":stroke-dasharray="circumference":stroke-dashoffset="dashOffset"stroke-linecap="butt"transform="rotate(-90 50 50)" /><!-- 中心文本 --><!-- <text class="progress-text" x="50" y="50" text-anchor="middle" dominant-baseline="middle">{{ Math.round(progress) }}%</text> --></svg><!-- 控制滑塊 --><!-- <div class="progress-controls"><inputtype="range"min="0"max="100"step="1"v-model.number="progress"class="progress-slider"@input="onProgressChange" /></div> --></div>
</template><script setup>
import { ref, computed } from 'vue';const props = defineProps({size: {type: Number,default: 48,},strokeWidth: {type: Number,default: 12,},backgroundColor: {type: String,default: '#D5DDEB', // 灰色背景},progressColor: {type: String,default: '#367AFB', // 藍色進度},// initialProgress: {// type: Number,// default: 0,// validator: (value) => value >= 0 && value <= 100,// },
});
const progress = defineModel({ required: true, validator: (value) => value >= 0 && value <= 100 });// const emit = defineEmits(['progress-change']);
// const progress = ref(props.initialProgress);
// watch(
// () => props.initialProgress,
// () => {
// progress.value = props.initialProgress;
// }
// );const radius = computed(() => 50 - props.strokeWidth / 2);
const circumference = computed(() => 2 * Math.PI * radius.value);
const dashOffset = computed(() => circumference.value * (1 - progress.value / 100));// const onProgressChange = () => {
// emit('progress-change', progress.value);
// };
</script><style scoped>
.circular-progress-container {display: flex;flex-direction: column;align-items: center;gap: 20px;width: fit-content;
}.progress-svg {display: block;
}.progress-background {transition: stroke 0.3s ease;
}.progress-foreground {transition: stroke-dashoffset 0.5s ease, stroke 0.3s ease;
}.progress-text {font-size: 20px;font-weight: bold;fill: #333;user-select: none;
}.progress-controls {width: 80%;max-width: 300px;
}.progress-slider {width: 100%;cursor: pointer;
}.progress-slider::-webkit-slider-thumb {-webkit-appearance: none;width: 18px;height: 18px;border-radius: 50%;background: v-bind('props.progressColor');cursor: pointer;
}.progress-slider::-moz-range-thumb {width: 18px;height: 18px;border-radius: 50%;background: v-bind('props.progressColor');cursor: pointer;
}
</style>
用法
<Circle v-model="dataState.finishRate"></Circle>
radius
計算半徑
const radius = computed(() => 50 - props.strokeWidth / 2);
因為設置了viewBox="0 0 100 100"
,所以是繪制在一個100*100的畫布上
stroke-dashoffset
屬性解釋
stroke-dashoffset
控制虛線模式的起始位置偏移量。結合 stroke-dasharray
,我們可以實現進度條效果:
stroke-dasharray
:定義虛線的模式(實線長度, 間隔長度)stroke-dashoffset
:定義虛線模式的起始偏移量
對于環形進度條:
- 設置
stroke-dasharray
為圓的周長(表示100%進度) - 通過
stroke-dashoffset
控制顯示比例
const radius = 50 - strokeWidth / 2; // 半徑
const circumference = 2 * Math.PI * radius; // 圓周長// 計算偏移量(0%進度時偏移量=周長,100%進度時偏移量=0)
const dashOffset = circumference * (1 - progress / 100);
- 初始狀態下,我們希望進度為0,也就是沒有顯示進度,那么我們應該將整個長劃偏移出去,即偏移量等于周長(dashoffset = circumference)。
- 隨著進度增加,我們希望顯示的部分越來越多,那么偏移量應該逐漸減少。當進度為100%時,偏移量為0(即沒有偏移,整個圓環顯示出來)。
因此,偏移量的計算公式為:
dashoffset = circumference * (1 - progress / 100)
stroke-linecap
控制線段端點的形狀
-
butt (默認值)
線段以方形結束,不超出端點
進度條看起來像被"切斷"的效果
適合需要精確對齊的場景
-
round
線段末端添加半圓形帽,半徑等于線寬的一半
使進度條兩端呈現圓潤效果
適合大多數進度條場景,視覺效果更柔和
-
square
線段末端添加方形帽,延伸長度等于線寬的一半
類似于 butt 但會稍微超出端點
適合需要整齊但略微延伸的效果
使用svg實現的優點
輕量、精確控制、動畫支持、矢量圖形、交互性