123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- <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.$t.uuid()形式动态生成id值,否则会报错
- // #ifdef MP-WEIXIN
- elBgId: 'tCircleProgressBgId',
- elId: 'tCircleProgressElId',
- // #endif
- // #ifndef MP-WEIXIN
- elBgId: this.$t.uuid(),
- elId: this.$t.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>
|