赞
踩
要绘制一个如此的进度条
uni-app使用canvas绘制时间刻度以及不显示问题处理
其实基本看参考链接学着搞, 不难的 . 主要是针对一些api的坑,要有了解就可以.
//0.1把画布先 逆时针旋转90度, 从0点绘制开始绘制
baseCtx.rotate(-90 * Math.PI / 180)
梳理一下流程, 如果要画一个圆环, 且要保证起始点是0点方向, 步骤是
1.画布逆时针旋转90度
2.画圆操作
3.恢复画布旋转角度(因为rotate()角度会叠加,为了防止计算混乱, 可以旋转画布)
(有人会问执行第三点
,不会把绘制的圆环又逆回去? 这里要明确的是, 画布是画布(即context), 绘制好的图像是绘制好的图像)
首先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>
<scoreLevelCanvas :score="277" :progress="0.68"></scoreLevelCanvas>
<scoreLevelCanvas :score="277" :progress="0.97"></scoreLevelCanvas>
<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>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。