# HarmonyOS 基于JSAPI自定义封装渐变圆环进度条组件
**Repository Path**: yango520/compontentCircle
## Basic Information
- **Project Name**: HarmonyOS 基于JSAPI自定义封装渐变圆环进度条组件
- **Description**: HarmonyOS 基于JSAPI自定义封装渐变圆环进度条组件
- **Primary Language**: JavaScript
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 2
- **Forks**: 0
- **Created**: 2022-01-26
- **Last Updated**: 2022-09-07
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# HarmonyOS 基于JSAPI自定义封装渐变圆环进度条组件
## 前言
圆环进度条组件在开发过程中是经常会用到的组件,比如在loading加载、升级过程,下载过程等等都需要用到。本文是基于HarmonyOS JSPAI开发,使用canvas画布封装的组件。方便直接引入上手使用。
## 效果展示

## 属性
| 属性名 | 类型 | 默认值 | 作用 |
| ---- | ---- | ---- | ---- |
| cWidth | Number | 200 | 外环宽度 |
| cHeight | Number | 200 | 外环高度 |
| staticScale | Number | 4 | 缩放级,值越大,越清晰,渲染压力越大 |
| cRate | Number | 0 | 当前进度 |
| lineWidth | Number | 20 | 圆环宽度 |
| cColor | String | #86C1FF,#2B5BF9 | 进度颜色(必须是hex),当只有一个颜色值时为纯色进度,最多只支持两个颜色,使用半角逗号分开 |
| bgColor | String | #e9ebed | 圆环背景颜色 |
| text | String | - | 显示的文本 |
| textSize | Number | - | 显示的文本字体大小 |
对应组件传入的prop参数
```javascript
props: {
// 圆环宽度
cWidth: {
type: Number,
default: 200,
},
// 圆环高度
cHeight: {
type: Number,
default: 200,
},
// 缩放级,值越大,越清晰,渲染压力越大
staticScale:{
type: Number,
default: 4,
},
// 显示文本
text: {
type: String,
default: '',
},
// 圆环宽度
lineWidth: {
type: Number,
default: 20
},
// 当前进度
cRate: {
type: Number,
default: 0
},
// 进度颜色,当只有一个颜色值时为纯色进度,最多只支持两个颜色,使用半角逗号分开
cColor: {
type: String,
default: '#86C1FF,#2B5BF9'
},
// 背景色
bgColor:{
type: String,
default: '#e9ebed'
},
// 文字大小
textSize: {
type: Number,
default: 32
}
},
```
## 调用组件
``` javascript
```
## 实现原理
我们知道,在canvas中可以实现线性渐变和径向渐变,但是这些渐变都不够美观并且无法实现根据圆环方向线性渐变。
本文通过将圆环弧切片成若干等分绘制指定的颜色来实现的。
**下面拆分各个步骤**
- 创建canvas对象并且设置canvas大小
- 使用canvas的arc() API绘制一个圆充当圆环背景,给lineWidth设置圆环宽度的值
- 计算两个颜色之间的渐变颜色值。确定需要切片的精度,来确定渐变颜色值之间的差级。
- 在原来的背景圆环上,再绘制一个圆弧,圆弧是根据开始位置和结束位置,然后绘制一段圆弧之间的所有切片
### 1. 初始化canvas
这里需要有个关键点,getContext(contextType, contextAttributes)方法可以传入两个参数
- contextType 上下文类型 可选 2d、webgl
- contextAttributes 上下文属性
**注意**:在HarmonyOS 的JSPAI官网文档没有提到这两个属性,但是案例上有看到`const ctx = el.getContext('2d', { antialias: true });`这样一句话。这里似乎跟webAPI的有些区别。
antialias属性是设置是否开启抗锯齿,但是在webAPI中,只有在上下文类型为webgl的时候才有这个属性。
先写个canvas标签
```html
```
再初始化canvas对象
```javascript
initCanvas(){
let canvas = this.$refs.canvas;
this.ctx = canvas.getContext('2d');
canvas.width = this.cWidth * this.staticScale;
canvas.height = this.cHeight * this.staticScale;
},
```
### 2. 绘制背景圆环
通过canvas的arc(x,y,r,sAngle,eAngle,counterclockwise)方法绘制一个圆
**参数值**
| 参数 | 描述 |
| :----------------- | :----------------------------------------------------------- |
| *x* | 圆的中心的 x 坐标。 |
| *y* | 圆的中心的 y 坐标。 |
| *r* | 圆的半径。 |
| *sAngle* | 起始角,以弧度计(弧的圆形的三点钟位置是 0 度)。 |
| *eAngle* | 结束角,以弧度计。 |
| *counterclockwise* | 可选。规定应该逆时针还是顺时针绘图。False = 顺时针,true = 逆时针。 |
在canvas绘制圆的其实位置是在x+r的位置开始,完整的圆弧为2*PI。因为我们要在最顶端开始绘制,所以我们的开始位置是1.5*PI,而我们的结束位置就不是2*PI了,而是1.5*PI + 2 * PI,这个才是绘制的完整的一个圆弧。最后

```javascript
this.ctx.beginPath();
this.ctx.lineCap = "round"; //向线条的每个末端添加圆形线帽
this.ctx.lineWidth = 20; //绘制圆的边框
this.ctx.strokeStyle = '#ff9800'; //绘制边框的颜色
this.ctx.arc(100,100,100,Math.PI * 1.5,(Math.PI * 1.5 * Math.PI * 2));
this.ctx.stroke();
```
上面代码我们这里绘制了一个圆心坐标在(100,100),半径为100,边框为20,边框颜色为 #ff9800的圆环

### 3. 实现圆弧切片绘制方法
#### 1. 获取两颜色之间的渐变值组
- 首先我们需要把获取到的hex色值转换成rgb三原色值,
- 然后通过计算开始颜色和结束颜色总差值来获取每一步的颜色值,
- 最后再转换成hex设置
将hex色值转换成rgb三原色值方法
```javascript
// 将hex表示方式转换为rgb表示方式(这里返回rgb数组模式)
hexToRgb(sColor){
var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
sColor = sColor.toLowerCase();
if(sColor && reg.test(sColor)){
if(sColor.length === 4){
var sColorNew = "#";
for(var i=1; i<4; i+=1){
sColorNew += sColor.slice(i,i+1).concat(sColor.slice(i,i+1));
}
sColor = sColorNew;
}
//处理六位的颜色值
var sColorChange = [];
for(let i=1; i<7; i+=2){
sColorChange.push(parseInt("0x"+sColor.slice(i,i+2)));
}
return sColorChange;
}else{
return sColor;
}
},
```
通过计算开始颜色和结束颜色总差值来获取每一步的颜色值
```javascript
/**
* @description: 封装颜色渐变之间值
* @param {String} startColor 开始颜色hex
* @param {Number} endColor 结束颜色hex
* @param {Number} step 渐变精度
* @return {Array}
*/
gradientColor(startColor,endColor,step){
let startRGB = this.hexToRgb(startColor);//转换为rgb数组模式
let endRGB = this.hexToRgb(endColor);
let sR = (endRGB[0]-startRGB[0])/step;//总差值
let sG = (endRGB[1]-startRGB[1])/step;
let sB = (endRGB[2]-startRGB[2])/step;
var colorArr = [];
for(var i=0;i
{{text}}
```
### css文件
```css
.circle-box{
position: relative;
}
.canvas{
width: 100%;
height: 100%;
}
.text{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
z-index: 10;
text-align: center;
}
```
## 总结
在开发过程中也遇到了一些坑,比如SDK版本问题:在SDK version7的时候渲染没有问题,但是IDE预览器经常崩溃掉。后来改回SDK6就好了。
以往在开发vue的时候,会直接把canvas对象赋值到data函数里,方便其他方法直接调用,但是在这里却发现不行。最后发现这里data并非是一个函数,而是一个对象,这里还没有深入研究。
后续在使用组件的时候未必一定需要使用text属性,可以使用slot插槽实现多样文本显示。