mrr.sj.front/components/circle-progress/circle-progress.vue

276 lines
6.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="circle-progress" :style="{ width: size + 'rpx', height: size + 'rpx' }">
<!-- 背景圆环 -->
<canvas
:type="canvasType"
class="progress-canvas"
:style="{ width: size + 'rpx', height: size + 'rpx' }"
:id="canvasId"
:canvas-id="canvasId"
@ready="onCanvasReady"
></canvas>
<!-- 进度文字 -->
<view class="progress-text" v-if="showText">
<slot>
<text class="text-value">{{ progress }}%</text>
</slot>
</view>
</view>
</template>
<script>
export default {
name: 'CircleProgress',
props: {
// 进度值0-100
progress: {
type: Number,
default: 0,
validator: value => value >= 0 && value <= 100
},
// 圆环大小
size: {
type: Number,
default: 200
},
// 圆环宽度
strokeWidth: {
type: Number,
default: 10
},
// 圆环颜色
color: {
type: String,
default: '#E8101E'
},
// 背景圆环颜色
backgroundColor: {
type: String,
default: '#F5F5F5'
},
// 是否显示进度文字
showText: {
type: Boolean,
default: true
},
// 是否显示动画
animation: {
type: Boolean,
default: true
},
// 动画时长(毫秒)
duration: {
type: Number,
default: 1000
}
},
data() {
return {
canvasId: 'circleProgress_' + Math.random().toString(36).substr(2, 9),
ctx: null,
canvas: null,
animationFrame: null,
currentProgress: 0,
canvasType: '2d'
}
},
watch: {
progress: {
handler(newVal) {
if (this.animation) {
this.animateProgress(newVal)
} else {
this.currentProgress = newVal
this.drawProgress()
}
},
immediate: true
}
},
mounted() {
// #ifdef H5
this.canvasType = ''
// #endif
this.initCanvas()
},
beforeDestroy() {
if (this.animationFrame) {
cancelAnimationFrame(this.animationFrame)
}
},
methods: {
// 初始化画布
async initCanvas() {
const query = uni.createSelectorQuery().in(this)
query.select(`#${this.canvasId}`)
.fields({ node: true, size: true })
.exec((res) => {
if (!res[0]) return
// #ifdef H5
this.canvas = res[0].node || res[0].canvas
if (!this.canvas) {
this.canvas = document.getElementById(this.canvasId)
}
// #endif
// #ifndef H5
this.canvas = res[0].node
// #endif
if (!this.canvas) return
this.ctx = this.canvas.getContext('2d')
if (!this.ctx) return
// 设置画布大小
const dpr = uni.getSystemInfoSync().pixelRatio
this.canvas.width = this.size * dpr
this.canvas.height = this.size * dpr
this.ctx.scale(dpr, dpr)
// 设置初始进度
this.currentProgress = this.progress
// 绘制初始进度
this.drawProgress()
})
},
// 绘制进度
drawProgress() {
if (!this.ctx) return
const center = this.size / 2
const radius = (this.size - this.strokeWidth) / 2
// 清空画布
this.ctx.clearRect(0, 0, this.size, this.size)
// 绘制背景圆环
this.ctx.beginPath()
this.ctx.arc(center, center, radius, 0, Math.PI * 2)
this.ctx.strokeStyle = this.backgroundColor
this.ctx.lineWidth = this.strokeWidth
this.ctx.lineCap = 'round'
this.ctx.stroke()
// 绘制进度圆环
if (this.currentProgress > 0) {
const endAngle = (this.currentProgress / 100) * Math.PI * 2
// 创建渐变色
const gradient = this.ctx.createLinearGradient(0, 0, this.size, 0)
gradient.addColorStop(0, this.color)
gradient.addColorStop(1, this.color)
this.ctx.beginPath()
this.ctx.arc(center, center, radius, -Math.PI / 2, endAngle - Math.PI / 2)
this.ctx.strokeStyle = gradient
this.ctx.lineWidth = this.strokeWidth
this.ctx.lineCap = 'round'
this.ctx.stroke()
// 添加发光效果
this.ctx.shadowColor = this.color
this.ctx.shadowBlur = 10
this.ctx.stroke()
this.ctx.shadowBlur = 0
}
},
// 动画效果
animateProgress(targetProgress) {
if (this.animationFrame) {
cancelAnimationFrame(this.animationFrame)
}
const startProgress = this.currentProgress
const startTime = Date.now()
// 使用缓动函数
const easeOutQuart = (x) => {
return 1 - Math.pow(1 - x, 4)
}
const animate = () => {
const currentTime = Date.now()
const elapsed = currentTime - startTime
if (elapsed >= this.duration) {
this.currentProgress = targetProgress
this.drawProgress()
return
}
const progress = easeOutQuart(elapsed / this.duration)
this.currentProgress = startProgress + (targetProgress - startProgress) * progress
this.drawProgress()
this.animationFrame = requestAnimationFrame(animate)
}
animate()
}
}
}
</script>
<style>
.circle-progress {
position: relative;
display: inline-flex;
justify-content: center;
align-items: center;
}
.progress-canvas {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
.progress-text {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.text-value {
font-size: 32rpx;
color: #333333;
font-weight: 500;
}
/* H5适配 */
/* #ifdef H5 */
.progress-canvas {
transform: scale(1);
}
/* #endif */
/* APP适配 */
/* #ifdef APP-PLUS */
.progress-canvas {
transform: scale(1);
}
/* #endif */
/* 小程序适配 */
/* #ifdef MP-WEIXIN */
.progress-canvas {
transform: scale(1);
}
/* #endif */
</style>