hldy_app_mini/pages/material/component/gress.vue

205 lines
4.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div
class="circle-progress"
:style="{
width: '5vw',
height: '5vw',
}"
>
<!-- 背景圆环 -->
<div class="circle-bg" :style="{ borderColor: bgColor }"></div>
<!-- 进度圆环容器用于实现圆角 -->
<div class="circle-bar-wrapper">
<!-- 进度圆环核心 -->
<div
class="circle-bar"
:style="{
background: `conic-gradient(${color} ${animateProgress}%, transparent ${animateProgress}%)`,
'--thickness': `${thickness}px`,
'--half-thickness': `${thickness / 2}px`,
}"
></div>
</div>
<!-- 中间文字 -->
<div class="circle-content" v-if="showText">
<div class="progress-number">{{ Math.round(animateProgress) }}%</div>
<div class="progress-label">进度</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref, onMounted, watch } from 'vue';
/**
* 纯CSS环形进度条组件APP/移动端专用)
* 适配设计图样式,带数字加载动效
*/
interface ProgressProps {
/** 进度 0~100 */
progress?: number;
/** 进度条颜色 */
color?: string;
/** 背景圆环颜色 */
bgColor?: string;
/** 圆环粗细(优化后默认更细) */
thickness?: number;
/** 是否显示中间文字 */
showText?: boolean;
/** 动画时长(秒) */
duration?: number;
}
// 定义Propsthickness默认改为1px更细
const props = withDefaults(defineProps<ProgressProps>(), {
progress: 0,
color: '#409EFF', // 匹配设计图蓝色
bgColor: '#E5E5E5', // 灰色背景(提高不透明度,保证显示清晰)
thickness: 1, // 进度条更细默认1px
showText: true,
duration: 1.5, // 动画时长
});
// 限制进度在0-100之间
const realProgress = computed(() => {
const p = Number(props.progress);
return isNaN(p) ? 0 : Math.max(0, Math.min(100, p));
});
// 动画进度值
const animateProgress = ref(0);
// 数字加载动画
const startProgressAnimation = () => {
const target = realProgress.value;
const duration = props.duration * 1000;
const frameRate = 60;
const totalFrames = duration / (1000 / frameRate);
const increment = target / totalFrames;
let currentFrame = 0;
const timer = setInterval(() => {
currentFrame++;
animateProgress.value += increment;
if (currentFrame >= totalFrames || animateProgress.value >= target) {
animateProgress.value = target;
clearInterval(timer);
}
}, 1000 / frameRate);
};
// 监听进度变化重新执行动画
watch(realProgress, () => {
animateProgress.value = 0;
startProgressAnimation();
}, { immediate: false });
// 组件挂载时执行动画
onMounted(() => {
startProgressAnimation();
});
</script>
<style scoped>
.circle-progress {
position: relative;
flex-shrink: 0;
aspect-ratio: 1/1;
/* 增加立体阴影,提升层次感 */
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
border-radius: 50%;
/* 背景色保证阴影显示完整 */
background: #fff;
}
/* 背景圆环(完整显示灰色环) */
.circle-bg {
width: 100%;
height: 100%;
border-radius: 50%;
border: 3px solid #E8E9EA;
box-sizing: border-box;
opacity: 1;
z-index: 2;
position: relative;
}
/* 进度圆环容器(核心:实现进度条圆角) */
.circle-bar-wrapper {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 50%;
z-index: 3;
/* 裁剪超出部分,保证圆角效果 */
overflow: hidden;
}
/* 进度圆环(核心) */
.circle-bar {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 50%;
/* 环形核心:锥形渐变 + 遮罩 */
-webkit-mask: radial-gradient(
transparent calc(50% - 3px),
black calc(50% - 3px)
);
mask: radial-gradient(
transparent calc(50% - 3px),
black calc(50% - 3px)
);
/* 进度条圆角关键:通过伪元素实现 */
&::after {
content: '';
position: absolute;
top: 3px;
left: 3px;
right: 3px;
bottom: 3px;
border-radius: 50%;
box-shadow: inset 0 0 0 13px rgba(255, 255, 255, 1);
}
/* 进度条末端圆角优化 */
clip-path: circle(50% at center);
}
/* 中间文字容器 */
.circle-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80%;
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 3; /* 文字层级最高 */
}
/* 百分比数字样式 - 匹配设计图 */
.progress-number {
font-size: 1.2vw; /* 适配5vw容器的字体大小 */
font-weight: 600;
color: #333;
line-height: 1;
margin-bottom: 0.2vw;
}
/* 进度标签样式 - 匹配设计图 */
.progress-label {
font-size: 1.1vw; /* 适配5vw容器的字体大小 */
color: #666;
line-height: 1;
}
</style>