调用插件:
<circle-progress-bar :pro="84/100" :border_back_color="'#297DFE'" :border_color="'#FB8F23'">
{{84}}%
</circle-progress-bar>
添加插件引用:
<script>
import CircleProgressBar from '../../components/circle-progress-bar/circle-progress-bar.vue'
export default {
components: {
Axxx,
CircleProgressBar,
},
</script>
circle-progress-bar.vue
<template>
<view class="circle-progress-bar"
:style="{
width: sunit(size),
height: sunit(size),
}">
<view class="circle" :change:prop="animateModule.pro" :prop="cpro"
:data-animate="animate"
:style="{
transform: `rotate(${start * 360 + 45}deg)`,
border: `${sunit(border_width)} solid ${border_color}`,
}">
</view>
<view class="bg" v-if="background"
:style="{
background: background,
}"></view>
<view class="border-back" v-if="border_back_color"
:style="{
border: `calc(${sunit(border_width)} - 1px) solid ${border_back_color}`
}"></view>
<view class="center">
<slot :pro="cpro"></slot>
</view>
</view>
</template>
<script module="animateModule" lang="wxs">
var Timing = {
easeIn: function easeIn(pos) {
return Math.pow(pos, 3);
},
easeOut: function easeOut(pos) {
return Math.pow(pos - 1, 3) + 1;
},
easeInOut: function easeInOut(pos) {
if ((pos /= 0.5) < 1) {
return 0.5 * Math.pow(pos, 3);
} else {
return 0.5 * (Math.pow(pos - 2, 3) + 2);
}
},
linear: function linear(pos) {
return pos;
}
};
//#ifdef MP
function setTimeout(t, cb, d) {
if (d > 0) {
var s = getDate().getTime();
var fn = function () {
if (getDate().getTime() - s > d) {
cb && cb();
} else
t.requestAnimationFrame(fn);
}
fn();
}
else
cb && cb();
}
//#endif
function Animation(opts) {
opts.duration = typeof opts.duration === 'undefined' ? 1000 : opts.duration;
opts.timing = opts.timing || 'linear';
var delay = 17;
function createAnimationFrame() {
if (typeof setTimeout !== 'undefined') {
return function(step, delay) {
//#ifndef MP
setTimeout(function() {
var timeStamp = +new Date();
step(timeStamp);
}, delay);
//#endif
//#ifdef MP
setTimeout(opts.instance, function () {
var timeStamp = getDate()
step(timeStamp);
}, delay)
//#endif
};
} else if (typeof requestAnimationFrame !== 'undefined') {
return requestAnimationFrame;
} else {
return function(step) {
step(null);
};
}
};
var animationFrame = createAnimationFrame();
var startTimeStamp = null;
var _step = function step(timestamp) {
if (timestamp === null) {
opts.onProcess && opts.onProcess(1);
opts.onAnimationFinish && opts.onAnimationFinish();
return;
}
if (startTimeStamp === null) {
startTimeStamp = timestamp;
}
if (timestamp - startTimeStamp < opts.duration) {
var process = (timestamp - startTimeStamp) / opts.duration;
var timingFunction = Timing[opts.timing];
process = timingFunction(process);
opts.onProcess && opts.onProcess(process);
animationFrame(_step, delay);
} else {
opts.onProcess && opts.onProcess(1);
opts.onAnimationFinish && opts.onAnimationFinish();
}
};
animationFrame(_step, delay);
}
function getPath(deg) {
var path = '50% 50%'
//各个锚点
var ps = ['0% 0%', '100% 0%', '100% 100%', '0% 100%']
var ps1 = path + ',' + ps[0]
var ps2 = ps1 + ',' + ps[1]
var ps3 = ps2 + ',' + ps[2]
var ps4 = ps3 + ',' + ps[3]
var ops = [
function(per) { return ps1 + ',' + (per + '% 0%') },
function(per) { return ps2 + ',' + ('100% ' + per + '%') },
function(per) { return ps3 + ',' + (100 - per) + '% 100%' },
function(per) { return ps4 + ',' + '0% ' + (100 - per) + '%' },
]
if (deg == 0) {
return 'polygon(50% 50%, 50% 0%)'
}
else if (deg % 360 == 0) {
return ''
}
var idx = parseInt(deg / 90) % 4
var pdeg = deg % 90
var per = pdeg / 90 * 100
if(ops[idx]) {
return 'polygon(' + ops[idx](per) + ')'
}
else {
return ''
}
}
function setDeg(newValue, oldValue, ownerInstance, instance) {
var odeg = oldValue * 360
var deg = newValue * 360
var offset = deg - odeg
var ds = instance.getDataset()
if(!ds.animate) {
var path = getPath(deg)
instance.setStyle({
'clip-path': path,
})
return
}
Animation({
instance: ownerInstance,
timing: 'easeInOut',
duration: 300,
onProcess: function onProcess(process) {
var pdeg = odeg + process * offset
var path = getPath(pdeg)
var com = ownerInstance.selectComponent('.circle');
com.setStyle({
'clip-path': path,
})
},
onAnimationFinish: function onAnimationFinish() {}
});
}
module.exports = {
pro: setDeg,
}
</script>
<script>
export default {
props: {
pro: {
type: Number,
default: 0
},
//起始位置 0-1
start: {
type: Number,
default: 0,
},
//圆形大小
size: {
type: Number,
default: 200
},
//线宽度
border_width: {
type: Number,
default: 20
},
//线颜色
border_color: {
type: String,
default: '#07C160',
},
//线背景色
border_back_color: {
type: String,
},
//中心内容背景色
background: {
type: String,
},
//单位
unit: {
type: String,
default: 'rpx',
},
//是否启用动画
animate:{
type: Boolean,
default: true,
}
},
data() {
return {
cpro: 0,
}
},
watch: {
pro(val) {
this.cpro = val
}
},
mounted() {
this.cpro = this.pro
},
methods: {
sunit(num) {
if(typeof num === 'number') {
return num + this.unit
}
}
}
}
</script>
<style scoped lang="scss">
.circle-progress-bar{
position: relative;
}
.circle, .bg, .border-back {
height: 100%;
width: 100%;
border-radius: 50%;
position: absolute;
box-sizing: border-box;
}
.circle{
z-index: 1;
}
.border-back{
height: calc(100% - 1px);
width: calc(100% - 1px);
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.point {
position: absolute;
border-radius: 50%;
z-index: 1;
}
.center{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 2;
}
</style>