使用css和定时器实现环形刻度进度条
因为项目还需要开发微信小程序端,所以兼容性是要解决的一个大问题,原本的环形刻度进度条是使用canvas写的,但是在小程序端问题就显露出来了,因为在小程序中canvas是属于原生组件,是不允许放置在scroll-view里面的,就算只是使用css的属性overflow让页面可以滚动,在页面滚动的时候,进度条还是会发生错位的现象(真机上调试),另外在app端测试的时候ios14系统上会显示不全(能力有限,无法解决),所以只能用css和js定时器重新写一个环形刻度进度条
效果图
原理解析
主要原理就是利用js定时器和css属性transfrom:rotate(xdeg)属性来实现遮罩动画,利用ps中的图层原理和overflow:hidden来实现圆环轨道
原理图解
圆形容器画轨道制作遮罩层遮罩动画
源码
html
<template>
<view class="progress_box">
<view class="inner_box">
<view class="progress_circle position_view">
<view class="p_left">
<view class="left_mask" :style="{transform: 'rotate('+rotate2+'deg)'}"></view>
</view>
<view class="p_right">
<view class="right_mask" :style="{transform: 'rotate('+rotate1+'deg)'}"></view>
</view>
</view>
<view class="mask_circle position_view"></view>
<view class="progress_txt">
<view>
<view class="progress_info">
<text class="txt">{{progress_txt}}</text>
<text>%</text>
</view>
<text class="text-gray" :style="{fontSize:13 + 'px'}">里程超越用户</text>
</view>
</view>
<view class="yuanpan">
<!-- 刻度,一共40个刻度,刻度数量自己调整 -->
<view
class="kedu"
v-for="item in 40"
:key="item"
:style="{transform: 'rotate('+item*9+'deg)'}"
></view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
rotate1: 180,
rotate2: 180,
rotate1Inter: null,
rotate2Inter: null,
};
},
props: ['duration', 'progress_txt'],
mounted() {
// this.draw()
},
watch: {
progress_txt: function () {
this.rotate1 = 180;
this.rotate2 = 180;
this.draw();
},
},
methods: {
draw() {
let step = 360 / 100;
let progress = (180 / (this.duration / 2)) * (50 / 1000);
if (this.progress_txt > 50) {
let maxRotate = (this.progress_txt - 50) * step + 180;
this.rotate1Inter = setInterval(() => {
this.rotate1 = this.rotate1 + progress;
if (this.rotate1 >= 360) {
this.rotate1 = 0;
clearInterval(this.rotate1Inter);
this.rotate2Inter = setInterval(() => {
this.rotate2 = this.rotate2 + progress;
if (this.rotate2 >= maxRotate) {
// this.rotate2 = 0
clearInterval(this.rotate2Inter);
}
}, 50);
}
}, 50);
} else {
let maxRotate = this.progress_txt * step + 180;
this.rotate1Inter = setInterval(() => {
this.rotate1 = this.rotate1 + progress;
if (this.rotate1 >= maxRotate) {
clearInterval(this.rotate1Inter);
}
}, 50);
}
},
},
};
</script>
<style lang="scss" scoped>
.progress_box {
display: flex;
justify-content: center;
align-items: center;
.inner_box {
height: 300rpx;
width: 300rpx;
position: relative;
overflow: hidden;
border-radius: 50%;
.position_view {
position: absolute;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
}
.progress_circle {
position: absolute;
height: 300rpx;
width: 300rpx;
border-radius: 50%;
z-index: 1000;
overflow: hidden;
display: flex;
// background: #5AA2FC;
.p_left {
width: 150rpx;
height: 300rpx;
position: relative;
overflow: hidden;
background: linear-gradient(to left, #00f2fe, #4facfe);
.left_mask {
// top right bottom left
position: absolute;
top: 0;
left: 0rpx;
width: 300rpx;
height: 300rpx;
border-radius: 50%;
clip: rect(0rpx, 300rpx, 300rpx, 150rpx);
background: rgb(230, 227, 232);
}
}
.p_right {
width: 150rpx;
height: 300rpx;
background: linear-gradient(to right, #00f2fe, #4facfe);
position: relative;
overflow: hidden;
.right_mask {
position: absolute;
top: 0;
left: -150rpx;
width: 300rpx;
height: 300rpx;
border-radius: 50%;
clip: rect(0rpx, 150rpx, 300rpx, 0rpx);
background: rgb(230, 227, 232);
}
}
}
.mask_circle {
height: 254rpx;
width: 254rpx;
background: rgb(255, 255, 255);
border-radius: 50%;
z-index: 1001;
}
.progress_txt {
position: absolute;
top: 0;
left: 0;
width: 300rpx;
height: 300rpx;
font-size: 28rpx;
color: #999999;
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
}
.progress_info {
display: flex;
justify-content: center;
align-items: center;
font-size: 52rpx;
color: #333333;
font-size: 36rpx;
}
.txt {
text-align: center;
font-size: 76rpx !important;
font-weight: 500;
}
}
}
.yuanpan {
margin: 0;
padding: 0;
height: 300rpx;
height: 300rpx;
// position: relative;
position: absolute;
left: -6rpx;
}
.yuanpan .kedu {
width: 10rpx;
height: 30rpx;
background: white;
position: absolute;
left: 150rpx;
top: 0;
-webkit-transform-origin: center 152rpx;
z-index: 99999;
}
</style>