224 lines
6.2 KiB
Vue
224 lines
6.2 KiB
Vue
<template>
|
||
<view
|
||
class="tn-circle-progress-class tn-circle-progress"
|
||
:style="{
|
||
width: widthPx + 'px',
|
||
height: widthPx + 'px'
|
||
}"
|
||
>
|
||
<!-- 支付宝小程序不支持canvas-id属性,必须用id属性 -->
|
||
<!-- 默认圆环 -->
|
||
<canvas
|
||
class="tn-circle-progress__canvas-bg"
|
||
:canvas-id="elBgId"
|
||
:id="elBgId"
|
||
:style="{
|
||
width: widthPx + 'px',
|
||
height: widthPx + 'px'
|
||
}"
|
||
></canvas>
|
||
<!-- 进度圆环 -->
|
||
<canvas
|
||
class="tn-circle-progress__canvas"
|
||
:canvas-id="elId"
|
||
:id="elId"
|
||
:style="{
|
||
width: widthPx + 'px',
|
||
height: widthPx + 'px'
|
||
}"
|
||
></canvas>
|
||
<view class="tn-circle-progress__content">
|
||
<slot v-if="$slots.default || $slots.$default"></slot>
|
||
<view v-else-if="showPercent" class="tn-circle-progress__content__percent">{{ percent + '%' }}</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
name: 'tn-circle-progress',
|
||
props: {
|
||
// 进度(百分比)
|
||
percent: {
|
||
type: Number,
|
||
default: 0,
|
||
validator: val => {
|
||
return val >= 0 && val <= 100
|
||
}
|
||
},
|
||
// 圆环线宽
|
||
borderWidth: {
|
||
type: Number,
|
||
default: 14
|
||
},
|
||
// 整体圆的宽度
|
||
width: {
|
||
type: Number,
|
||
default: 200
|
||
},
|
||
// 是否显示条纹
|
||
striped: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
// 条纹是否运动
|
||
stripedActive: {
|
||
type: Boolean,
|
||
default: true
|
||
},
|
||
// 激活部分颜色
|
||
activeColor: {
|
||
type: String,
|
||
default: '#01BEFF'
|
||
},
|
||
// 非激活部分颜色
|
||
inactiveColor: {
|
||
type: String,
|
||
default: '#f0f0f0'
|
||
},
|
||
// 是否显示进度条内部百分比值
|
||
showPercent: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
// 圆环执行动画的时间,ms
|
||
duration: {
|
||
type: Number,
|
||
default: 1500
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
// 微信小程序中不能使用this.$tn.uuid()形式动态生成id值,否则会报错
|
||
// #ifdef MP-WEIXIN
|
||
elBgId: 'tCircleProgressBgId',
|
||
elId: 'tCircleProgressElId',
|
||
// #endif
|
||
// #ifndef MP-WEIXIN
|
||
elBgId: this.$tn.uuid(),
|
||
elId: this.$tn.uuid(),
|
||
// #endif
|
||
// 活动圆上下文
|
||
progressContext: null,
|
||
// 转换成px为单位的背景宽度
|
||
widthPx: uni.upx2px(this.width || 200),
|
||
// 转换成px为单位的圆环宽度
|
||
borderWidthPx: uni.upx2px(this.borderWidth || 14),
|
||
// canvas画圆的起始角度,默认为-90度,顺时针
|
||
startAngle: -90 * Math.PI / 180,
|
||
// 动态修改进度值的时候,保存进度值的变化前后值
|
||
newPercent: 0,
|
||
oldPercent: 0
|
||
}
|
||
},
|
||
watch: {
|
||
percent(newVal, oldVal = 0) {
|
||
if (newVal > 100) newVal = 100
|
||
if (oldVal < 0) oldVal = 0
|
||
|
||
this.newPercent = newVal
|
||
this.oldPercent = oldVal
|
||
setTimeout(() => {
|
||
// 无论是百分比值增加还是减少,需要操作还是原来的旧的百分比值
|
||
// 将此值减少或者新增到新的百分比值
|
||
this.drawCircleByProgress(oldVal)
|
||
}, 50)
|
||
}
|
||
},
|
||
created() {
|
||
// 赋值,用于加载后第一个画圆使用
|
||
this.newPercent = this.percent;
|
||
this.oldPercent = 0;
|
||
},
|
||
mounted() {
|
||
setTimeout(() => {
|
||
this.drawProgressBg()
|
||
this.drawCircleByProgress(this.oldPercent)
|
||
}, 50)
|
||
},
|
||
methods: {
|
||
// 绘制进度条背景
|
||
drawProgressBg() {
|
||
let ctx = uni.createCanvasContext(this.elBgId, this)
|
||
// 设置线宽
|
||
ctx.setLineWidth(this.borderWidthPx)
|
||
// 设置颜色
|
||
ctx.setStrokeStyle(this.inactiveColor)
|
||
ctx.beginPath()
|
||
let radius = this.widthPx / 2
|
||
ctx.arc(radius, radius, radius - this.borderWidthPx, 0, 360 * Math.PI / 180, false)
|
||
ctx.stroke()
|
||
ctx.draw()
|
||
},
|
||
// 绘制圆弧的进度
|
||
drawCircleByProgress(progress) {
|
||
// 如果已经存在则拿来使用
|
||
let ctx = this.progressContext
|
||
if (!ctx) {
|
||
ctx =uni.createCanvasContext(this.elId, this)
|
||
this.progressContext = ctx
|
||
}
|
||
ctx.setLineCap('round')
|
||
// 设置线条宽度和颜色
|
||
ctx.setLineWidth(this.borderWidthPx)
|
||
ctx.setStrokeStyle(this.activeColor)
|
||
// 将总过渡时间除以100,得出每修改百分之一进度所需的时间
|
||
let preSecondTime = Math.floor(this.duration / 100)
|
||
// 结束角的计算依据为:将2π分为100份,乘以当前的进度值,得出终止点的弧度值,加起始角,为整个圆从默认的
|
||
let endAngle = ((360 * Math.PI / 180) / 100) * progress + this.startAngle
|
||
let radius = this.widthPx / 2
|
||
ctx.beginPath()
|
||
ctx.arc(radius, radius, radius - this.borderWidthPx, this.startAngle, endAngle, false)
|
||
ctx.stroke()
|
||
ctx.draw()
|
||
|
||
// 如果变更后新值大于旧值,意味着增大了百分比
|
||
if (this.newPercent > this.oldPercent) {
|
||
// 每次递增百分之一
|
||
progress++
|
||
// 如果新增后的值,大于需要设置的值百分比值,停止继续增加
|
||
if (progress > this.newPercent) return
|
||
} else {
|
||
progress--
|
||
if (progress < this.newPercent) return
|
||
}
|
||
setTimeout(() => {
|
||
// 定时器,每次操作间隔为time值,为了让进度条有动画效果
|
||
this.drawCircleByProgress(progress)
|
||
}, preSecondTime)
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
|
||
.tn-circle-progress {
|
||
position: relative;
|
||
/* #ifndef APP-NVUE */
|
||
display: inline-flex;
|
||
/* #endif */
|
||
align-items: center;
|
||
justify-content: center;
|
||
background-color: transparent;
|
||
|
||
&__canvas {
|
||
position: absolute;
|
||
|
||
&-bg {
|
||
position: absolute;
|
||
}
|
||
}
|
||
|
||
&__content {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
&__percent {
|
||
font-size: 28rpx;
|
||
}
|
||
}
|
||
}
|
||
</style>
|