vue+svg圆形进度条组件
- 一、实现思路
- 二、ProgressCircle.vue
- 三、父组件使用
- 四、实现效果
一、实现思路
使用svg的circle元素画两个圆形,一个圆形控制进度,一个绘制底色
二、ProgressCircle.vue
代码示例:
<template><!-- 圆形进度条控件 --><div :class="['progress-circle', `p-circle${randomNumber}`]"><svg :width="diameter" :height="diameter"><circleclass="progress-background":r="radius":cy="center":cx="center":stroke-width="strokeWidth":stroke="color.notOccupied"fill="none"/><circleclass="progress-line":r="radius":cy="center":cx="center":stroke-width="strokeWidth":stroke="color.occupy"stroke-linejoin="round"stroke-linecap="round"fill="none":stroke-dasharray="dashArray":stroke-dashoffset="dashOffset"/></svg><div class="progress-container"><slot></slot></div></div>
</template><script>
import { randomString } from '@/utils/index';
export default {props: {strokeWidth: { type: Number, default: 10, validator: value => value > 0 }, //线条宽度color: { type: Object, default: () => ({ occupy: '#1593ff', notOccupied: '#e0e0e0' }) }, //占比颜色percentage: { type: Number, required: true, validator: value => value >= 0 && value <= 100 }, //百分比1-100basisState: { type: String, default: 'width', validator: value => ['width', 'height'].includes(value) } // 默认以父元素宽度为直径长},data() {return {parentSize: 0,updateTimer: null,randomNumber: null};},computed: {// 直径长度diameter() {return this.parentSize >= this.strokeWidth ? this.parentSize : this.strokeWidth;},// 半径radius() {return (this.diameter - this.strokeWidth) / 2; // 减去 stroke-width 的一半,以确保圆环在 SVG 内部完全可见},// 圆心center() {return this.diameter / 2;},// 周长 2πrcircumference() {return 2 * Math.PI * this.radius;},// 虚线样式,间隔为一个周长dashArray() {return this.circumference;},// 虚线与路径起点之间的偏移量(周长的剩余占比)dashOffset() {return this.circumference - (this.circumference * this.percentage) / 100;}},created() {this.randomNumber = randomString(10); //生成随机字符串作为类名,确保多个组件存在时不会冲突},methods: {updateParentSize() {this.$nextTick(() => {this.updateTimer && clearTimeout(this.updateTimer);this.updateTimer = setTimeout(() => {const child = document.querySelector(`.p-circle${this.randomNumber}`);const parent = child.parentNode;this.parentSize = this.basisState == 'width' ? parent.offsetWidth : parent.offsetHeight; // 圆环大小以父元素宽度或高度作为直径}, 300);});}},mounted() {this.updateParentSize();window.addEventListener('resize', this.updateParentSize); // 监听窗口大小变化},beforeDestroy() {this.updateTimer && clearTimeout(this.updateTimer);window.removeEventListener('resize', this.updateParentSize);}
};
</script><style lang="scss" scoped>
.progress-circle {position: relative;.progress-container {position: absolute;width: 100%;height: 100%;top: 0;left: 0;}
}
.progress-background {transform: rotate(-90deg);// 默认情况下,SVG的圆弧是从3点钟方向(即右侧)开始绘制的// 通过旋转-90度,可以将起始位置调整到12点钟方向(即顶部)transform-origin: 50% 50%;
}
.progress-line {transform: rotate(-90deg);transform-origin: 50% 50%;
}
</style>
三、父组件使用
<template><div class="head-progress"><ProgressCircle :percentage="Number(extraParams.confirmRate || 0)"><div class="head_cont"><span class="he_text">开票进度</span><span class="he_prec">{{ extraParams.confirmRate || '0.00' }}%</span></div></ProgressCircle></div>
</template><script>
export default {components: {ProgressCircle: () => import('@/components/svgGraphic/progressCircle.vue')}
};
</script><style lang="scss" scoped>
.head-progress {width: 100px;height: 100px;margin-right: 30px;.head_cont {width: 100%;height: 100%;display: flex;flex-direction: column;align-items: center;justify-content: center;.he_text {font-size: 14px;color: #7180b0;}.he_prec {font-weight: 600;font-size: 16px;color: #0e1119;line-height: 28px;}}
}
</style>