
1.组件
c
<template>
<view class="circle-progress-container">
<!-- 画布容器 -->
<view class="canvas-wrapper">
<canvas canvas-id="bgCanvas" class="progress-canvas" :style="{ width: size + 'px', height: size + 'px' }"></canvas>
<canvas canvas-id="progressCanvas" class="progress-canvas"
:style="{ width: size + 'px', height: size + 'px' }"></canvas>
</view>
<!-- 修正后的文字容器 -->
<view class="text-wrapper" :style="{
width: size + 'px',
height: size + 'px'
}">
<view class="text-content">
<view class="score-line">
<text class="current-value">{{ currentValue.toFixed(1) }}</text>
<text class="max-value">/{{ maxValue }}</text>
</view>
<text class="progress-label">总成绩</text>
</view>
</view>
</view>
</template>
<script lang="ts">
import { Vue, Component, Prop, Watch } from 'vue-property-decorator'
@Component
export default class CircleProgress extends Vue {
@Prop({ default: 504.5 }) readonly currentValue!: number
@Prop({ default: 750 }) readonly maxValue!: number
@Prop({ default: 200 }) readonly size!: number
@Prop({ default: 12 }) readonly strokeWidth!: number
@Prop({ default: '#4CAF50' }) readonly progressColor!: string
@Prop({ default: '#e0f7e0' }) readonly bgColor!: string
private ctxBg: UniApp.CanvasContext | null = null
private ctxProgress: UniApp.CanvasContext | null = null
mounted() {
this.$nextTick(() => {
this.initCanvas()
})
}
@Watch('currentValue')
onValueChange() {
this.drawProgress()
}
private initCanvas() {
this.ctxBg = uni.createCanvasContext('bgCanvas', this)
this.ctxProgress = uni.createCanvasContext('progressCanvas', this)
this.drawBackground()
this.drawProgress()
}
private drawBackground() {
if (!this.ctxBg) return
const center = this.size / 2
const radius = center - this.strokeWidth / 2
this.ctxBg.beginPath()
this.ctxBg.setLineWidth(this.strokeWidth)
this.ctxBg.setStrokeStyle(this.bgColor)
this.ctxBg.arc(center, center, radius, 0, 2 * Math.PI)
this.ctxBg.stroke()
this.ctxBg.draw()
}
private drawProgress() {
if (!this.ctxProgress) return
const center = this.size / 2
const radius = center - this.strokeWidth / 2
const endAngle = (2 * Math.PI * this.currentValue) / this.maxValue - Math.PI / 2
this.ctxProgress.clearRect(0, 0, this.size, this.size)
this.ctxProgress.beginPath()
this.ctxProgress.setLineWidth(this.strokeWidth)
this.ctxProgress.setStrokeStyle(this.progressColor)
this.ctxProgress.setLineCap('round')
this.ctxProgress.arc(center, center, radius, -Math.PI / 2, endAngle)
this.ctxProgress.stroke()
this.ctxProgress.draw()
}
}
</script>
<style lang="less" scoped>
.circle-progress-container {
position: relative;
display: inline-block;
/* 改为inline-block避免flex影响 */
.canvas-wrapper {
position: relative;
width: 100%;
height: 100%;
.progress-canvas {
position: absolute;
left: 0;
top: 0;
}
}
/* 修正后的文字容器样式 */
.text-wrapper {
position: absolute;
top: 0;
left: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
.text-content {
display: flex;
flex-direction: column;
align-items: center;
transform: translateY(-5px);
/* 微调垂直居中 */
.score-line {
display: flex;
align-items: baseline;
.current-value {
font-size: 24px;
font-weight: bold;
color: #000;
}
.max-value {
font-size: 16px;
color: #999;
}
}
.progress-label {
font-size: 16px;
color: #4CAF50;
margin-top: 4px;
}
}
}
}
</style>
2、使用
c
<view :style="'width: ' + windowHeight + 'rpx; height: ' + windowHeight + 'rpx;'">
<circle-pr :current-value="elseScore" :size="windowHeight / 2" :max-value="750"
progress-color="#4CAF50"></circle-pr>
</view>
<script lang="ts">
import CirclePr from '../components/circlePro.vue';
@Component({
components: {
CirclePr
}
})
elseScore: number = 200
windowHeight = 600
onLoad() {
var that = this
wx.getSystemInfo({
success: function (res: any) {
// 屏幕宽度、高度
// 高度,宽度 单位为px
that.windowHeight = (res.windowHeight - 60) / 2;
console.log(that.windowHeight + '获取宽度')
},
});
this.elseScore = 504.5
}