当前位置:   article > 正文

[uni-app] canvas绘制圆环进度条_uniapp 环形进度条

uniapp 环形进度条

需求

要绘制一个如此的进度条
在这里插入图片描述

参考链接

uni-app使用canvas绘制时间刻度以及不显示问题处理

官网api

基本问题的处理

其实基本看参考链接学着搞, 不难的 . 主要是针对一些api的坑,要有了解就可以.

1:画布旋转的问题

在这里插入图片描述

2:注意arc()的起始位置是3点钟方向

在这里插入图片描述

3: 如果绘制1.9*Matn.PI的圆环, 要保证其实位置在0点方向?

在这里插入图片描述

			//0.1把画布先 逆时针旋转90度, 从0点绘制开始绘制
				baseCtx.rotate(-90 * Math.PI / 180)
  • 1
  • 2

梳理一下流程, 如果要画一个圆环, 且要保证起始点是0点方向, 步骤是
1.画布逆时针旋转90度
2.画圆操作
3.恢复画布旋转角度(因为rotate()角度会叠加,为了防止计算混乱, 可以旋转画布)

(有人会问执行第三点,不会把绘制的圆环又逆回去? 这里要明确的是, 画布是画布(即context), 绘制好的图像是绘制好的图像)

在这里插入图片描述

4:小线段怎么画, 角度怎么处理?

首先360°的圆,分成10份, 角度单位是36°
那么处理办法也是一样的
for循环内
1.画布旋转36° / 72° / 108° …
2.绘制小线段
3.画布恢复 36° / 72° / 108° …

在这里插入图片描述
绘制出的10等分小线段已经完成, 想要做到如下图效果. 我们只要在for循环内, 选出i=0,6,8即可
(即 100% 60% 80%进度)
在这里插入图片描述
但是问题来了…
实际效果如下
在这里插入图片描述
好像不对了…

通过控制不同下标的小线段的绘制, 得到如下的分析图,
(小线段因为是基于moveTo/lineTo,绘制的)
在这里插入图片描述
当i越大,小线段的起始点与结束点的距离也越大, 所以i=0的时候, 小线段最短,
在这里插入图片描述
那么我们就发现, 他是从原始画布的90°方向开始绘制的,

我们为了要得到从0点位置那就是对画布进行逆时针的180°旋转
在这里插入图片描述

那么步骤就是
for循环内
1.画布旋转36°-180° / 72°-180° / 108°-180° …
2.绘制小线段
3.画布恢复 逆向的 36°-180° / 72°-180° / 108°-180° …

于是得到了

在这里插入图片描述
再进过处理

在这里插入图片描述

再把圆环补全,去掉不用的小线段
在这里插入图片描述
与效果图对比(UI图实际是有点错误的,但这不重要)
在这里插入图片描述
最终效果:

	<scoreLevelCanvas :score="277" :progress="0.4"></scoreLevelCanvas>
  • 1

在这里插入图片描述

<scoreLevelCanvas :score="277" :progress="0.68"></scoreLevelCanvas>
  • 1

在这里插入图片描述

<scoreLevelCanvas :score="277" :progress="0.97"></scoreLevelCanvas>
  • 1

在这里插入图片描述

源码

<template>
	<view class="score-level-box" :style="{'width':reactWH+'px','height':reactWH+'px'}">
		<!-- baseCanvas -->
		<canvas id="scoreLevelBase" canvas-id="scoreLevelBase"
			:style="{'width':reactWH+'px','height':reactWH+'px'}"></canvas>
		<!-- progressCanvas -->
		<canvas id="scoreLevelProgress" canvas-id="scoreLevelProgress"
			style="position: absolute;left: 0;top:0"
			:style="{'width':reactWH+'px','height':reactWH+'px'}"></canvas>
		<view class="score-view" :style="{'fontSize':scoreFontSize+'px','color':strokeColor}">
			{{scoreString}}
		</view>
	</view>
</template>

<script>
	export default {
		name: "scoreLevelCanvas",
		props: {
			// canvas视图的宽高(矩形->正方形)
			reactWH: {
				type: Number,
				default: () => 200,
			},
			// 进度
			progress: {
				type: Number,
				default: () => 0.4
			},
			score: {
				type: Number,
				default: () => 20,
			}
		},
		data() {
			return {
				baseCtx: null,
				progressCtx: null,
			};
		},
		computed: {
			// 计算中心点X
			centerPointX() {
				return this.reactWH / 2;
			},
			// 计算中心点Y
			centerPointY() {
				return this.reactWH / 2;
			},
			//计算半径
			radius() {
				return this.reactWH / 2 * 0.9;
			},
			//计算小线段绘制的起始 - 偏内0.1个半径
			dotLineMoveTo() {
				return this.reactWH / 2 * (0.9 - 0.1);
			},
			//计算小线段绘制的结束 - 偏外0.1个半径
			dotLineLineTo() {
				return this.reactWH / 2 * (0.9 + 0.1);
			},
			//计算进度环的厚度
			progressWidth() {
				// 进度环的厚度, 设置为半径的8%
				return (this.reactWH / 2) * 0.08;
			},
			//计算小线段的厚度
			dotLineWidth() {
				// 小线段的厚度, 同圆环厚度
				return (this.reactWH / 2) * 0.08;
			},
			//计算进度环颜色
			strokeColor() {
				let strokeColor = ""
				if (this.progress < 0.6) {
					strokeColor = "#EA532F"
				} else if (this.progress >= 0.6 && this.progress < 0.8) {
					strokeColor = "#F9B93C"
				} else if (this.progress >= 0.8) {
					strokeColor = "#4CBA85"
				}
				return strokeColor
			},
			//计算得分字段
			scoreString() {
				return (this.score || "") + "分"
			},
			//计算得分字体大小
			scoreFontSize() {
				return this.radius * 0.4
			}
		},
		mounted() {
			// 绘制 基础圆样式
			this.drawScoreLevelBaseView()
			// 绘制 动态进度圆
			this.drawScoreLevelProgressView()
			// 最终绘制 - draw()
			this.draw()
		},
		methods: {
			// 绘制 基础圆样式
			drawScoreLevelBaseView() {
				const baseCtx = uni.createCanvasContext("scoreLevelBase", this);
				baseCtx.save();
				// 把圆心移到矩形中心点
				baseCtx.translate(this.centerPointX, this.centerPointY);

				//0.1把画布先 逆时针旋转90度, 从0点绘制开始绘制
				baseCtx.rotate(-90 * Math.PI / 180)

				//0.2绘制圆心, 方便观察
				// baseCtx.beginPath()
				// baseCtx.setStrokeStyle('#090')
				// baseCtx.arc(0, 0, 3, 0, 2 * Math.PI)
				// baseCtx.stroke()

				//1.绘制基础圆
				baseCtx.beginPath()
				baseCtx.setStrokeStyle("#EAEAEA")
				baseCtx.setLineWidth(this.progressWidth)
				baseCtx.setLineCap('round')
				baseCtx.arc(0, 0, this.radius, 0, 2 * Math.PI)
				baseCtx.stroke()
				//1.1恢复旋转角度(目的是恢复画布)
				baseCtx.rotate(90 * Math.PI / 180)

				//2. 绘制小线段 (360°/10)
				for (var i = 0; i < 10; i++) {
					// 计算每个小线段的旋转角度- 顺时针旋转画布
					// 发现,小线段在原始画布下, 是从90°方向顺时针绘制的, 因此要逆时针旋转180°
					let rotateDeg = i * 36 - 180
					baseCtx.rotate(rotateDeg * Math.PI / 180)
					baseCtx.beginPath()
					baseCtx.setLineWidth(0.3) // 预设一个极细的厚度,
					baseCtx.setLineCap('round')
					baseCtx.setStrokeStyle('#EAEAEA')
					// baseCtx.moveTo(0, this.dotLineMoveTo - (i * 8)) // (i*8)为了测试方便,
					baseCtx.moveTo(0, this.dotLineMoveTo)
					baseCtx.lineTo(0, this.dotLineLineTo)
					// 保留 100% 60% 80%进度的小线段
					if (i == 0 || i == 6 || i == 8) {
						baseCtx.setLineWidth(this.dotLineWidth)
					}
					baseCtx.stroke()
					// 绘制完成小线段后, 恢复画布旋转角度;
					baseCtx.rotate(-rotateDeg * Math.PI / 180)
				}
				this.baseCtx = baseCtx;
			},
			// 绘制 进度圆
			drawScoreLevelProgressView() {
				const progressCtx = uni.createCanvasContext("scoreLevelProgress", this);
				progressCtx.save();
				// 把圆心移到矩形中心点
				progressCtx.translate(this.centerPointX, this.centerPointY);

				//0.1把画布先 逆时针旋转90度, 从0点绘制开始绘制
				progressCtx.rotate(-90 * Math.PI / 180)

				//0.2绘制圆心, 方便观察
				// progressCtx.beginPath()
				// progressCtx.setStrokeStyle('#113')
				// progressCtx.arc(0, 0, 3, 0, 2 * Math.PI)
				// progressCtx.stroke()

				//1.绘制基础圆


				progressCtx.beginPath()
				progressCtx.setStrokeStyle(this.strokeColor)
				progressCtx.setLineWidth(this.progressWidth)
				progressCtx.setLineCap('round')
				progressCtx.arc(0, 0, this.radius, 0, 2 * this.progress * Math.PI)
				progressCtx.stroke()
				//1.1恢复旋转角度(目的是恢复画布)
				progressCtx.rotate(90 * Math.PI / 180)
				this.progressCtx = progressCtx;
			},
			draw() {
				setTimeout(() => {
					this.baseCtx.draw();
					this.progressCtx.draw();
				}, 50)
			},
		}
	}
</script>

<style lang="scss">
	.score-level-box {
		position: relative;
		// background-color: #91f;

		.score-view {
			position: absolute;
			top: 50%;
			left: 50%;
			transform: translate(-50%, -50%);
			font-family: PingFangSC-Medium, PingFang SC;
			font-weight: 500;

		}
	}
</style>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号