纯css实现环形进度条+动画加载效果
写在最前面:
本文是小程序开发中,使用纯css+html实现的进度圆环动画加载效果(换成vue也是一样的)。
如果你的项目可以用echarts,建议还是用插件,手搓不易,这很难评。
实现效果如上图:
HTML部分
<view :class="styles.chargeBox">
<view :class="styles.clockFace">
<!-- 24 个刻度线 -->
<view
v-for="i in 24"
:key="i"
:class="[styles.tick]"
:style="{ transform: `rotate(${(i - 1) * 15}deg) translateY(-248rpx)` }">
<!-- 特殊刻度数字 -->
<text
v-if="isSpecialTick(i)"
:class="styles.tickNumber"
:style="{ transform: `rotate(${-((i - 1) * 15)}deg)` }"
>
{{ getTickNumber(i) }}
</text>
</view>
<!-- 环形轨道 -->
<view :class="targetProgress == 100 ? styles.orbitFinsh : styles.orbitTrack" :style="{ '--progress': `${currentProgress}` }">
<template v-if="targetProgress > 0 && targetProgress < 100">
<!-- 进度条圆角 -->
<view :class="styles.orbitEnd"></view>
<!-- 半径上的小圆点 -->
<view :class="styles.orbitCircle"></view>
</template>
<!-- 外层渐变圆环 -->
<view :class="styles.outerCircle">
<view :class="styles.circularTrack">
<!-- 最小内层圆环 -->
<view :class="styles.innerCircle">
<view :class="styles.measure">
<view :class="styles.num">{{ currentProgress }}<view :class="styles.unit">%</view></view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
js部分
const isSpecialTick = (i) => {
const angle = (i - 1) * 15;
return angle % 90 === 0; // 0°, 90°, 180°, 270°
};
const getTickNumber = (i) => {
const angle = (i - 1) * 15;
switch (angle) {
case 0:
return '50';
case 90:
return '75';
case 180:
return '0';
case 270:
return '25';
default:
return '';
}
};
const currentProgress = ref(0);
const targetProgress = ref(90);
// 挂载后执行动画
onMounted(() => {
const interval = setInterval(() => {
if (currentProgress.value >= targetProgress.value) {
clearInterval(interval);
return;
}
currentProgress.value++;
}, 10);
});
css部分
.chargeBox {
display: flex;
justify-content: center;
align-items: center;
position: relative;
margin-bottom: 16px;
z-index: 9;
.clockFace {
width: 496px;
height: 496px;
border-radius: 50%;
position: relative;
margin: 50px;
display: flex;
justify-content: center;
align-items: center;
}
.tick {
position: absolute;
width: 8px; // 刻度线的宽度
height: 8px; // 刻度线的高度
border-radius: 50%;
background-color: var(--color-font-light__3); // 刻度线的颜色
top: 50%; // 垂直居中
left: 50%; // 水平居中
transform-origin: center top; // 设置变换的原点
}
.specialTick {
width: 16px; // 刻度线的宽度
height: 16px; // 刻度线的高度
border-radius: 50%;
background-color: var(--color-font-light__1); // 特殊刻度线的颜色
}
.tickNumber {
position: absolute;
top: -35px; // 调整数字的位置
left: -5px;
transform: translate(-55%, -55%);
font-size: 24px;
line-height: 1;
color: #0000001F;
}
.orbitFinsh {
width: 450px;
height: 450px;
position: relative;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
animation: calc(var(--progress) * 1%) 1.5s linear infinite; /* 应用旋转和增长动画 */
background: radial-gradient(
transparent 0%,
#137DF5 calc(var(--progress) * 1%),
transparent calc(var(--progress) * 1%),
);
z-index: 1;
}
.orbitTrack {
width: 450px;
height: 450px;
position: relative;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
animation: calc(var(--progress) * 1%) 1.5s linear infinite; /* 应用旋转和增长动画 */
background: conic-gradient(
from 180deg,
#E7EEFE 0%,
#137DF5 calc(var(--progress) * 1%),
transparent calc(var(--progress) * 1%),
);
z-index: 1;
}
.orbitEnd {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 50%;
transform: rotate(calc(180deg + (var(--progress) * 3.6deg)));
transform-origin: center center;
animation: transform 1.5s linear infinite;
}
.orbitEnd::before {
position: absolute;
display: inline-block;
content: "";
width: 15px;
height: 16px;
border-radius: 50%;
background-color: #137DF5;
top: 0%;
left: 47.9%;
}
.orbitCircle {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 50%;
transform: rotate(calc(180deg + var(--progress) * 3.6deg));
animation: transform 1.5s linear infinite;
transform-origin: center center;
z-index: 2;
}
.orbitCircle::after {
position: absolute;
display: inline-block;
content: "";
width: 22px;
height: 22px;
border-radius: 50%;
border: 5.41px solid #fff;
background-color: #137DF5;
top: 8%;
left: 46.5%;
}
.outerCircle {
width: 420px;
height: 420px;
background: rgba(227,255,241,0.41);
box-shadow: inset 0px 0px 54px 0px rgba(99,191,255,0.48);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
position: relative;
overflow: visible;
.innerCircle {
width: 272px;
height: 272px;
background: radial-gradient(42% at 73% 53%, #FFFFFD 100%, #D2E2FF 100%);
box-shadow: 0px 22px 27px -14px rgba(54,110,244,0.08), 0px 43px 65px 5px rgba(54,110,244,0.04), 0px 16px 81px 14px rgba(54,110,244,0.23), inset 0px 0px 32px 0px #FFFFFF;
border-radius: 50%;
z-index: 10;
display: flex;
justify-content: center;
align-items: center;
}
.circularTrack {
width: 272px;
height: 272px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
background: radial-gradient(
#FFFFFD 0%,
#D2E2FF 100%,
);
}
}
.measure {
font-family: Bebas, Bebas;
font-weight: 500;
color: #137DF5;
display: flex;
justify-content: flex-end;
align-items: flex-end;
.num {
font-size: 96px;
line-height: 96px;
position: relative;
}
.unit {
position: absolute;
bottom: 0;
right: -30px;
font-size: 36px;
line-height: 46px;
text-align: left;
display: inline-block;
}
}
}
end!
希望记录的问题能帮助到你~