微信小程序制作圆形进度条
1. 建立文件夹
选择一个目录建立一个文件夹,比如 mycircle 吧,另外把对应 page 的相关文件都建立出来,包括 js,json,wxml 和 wxcc。
2. 开启元件属性
在 mycircle.json中开启 component 属性,确定我们这个页面是一个可被调用的元件。
{
"component": true,
"usingComponents": {}
}
3. 建立 XML 样式
在 mycircle.wxml文件中设计空间的样式及接口参数,也要注意一下 wxss 文件的样式配合。
wxml 文件内容为:
<view class="circle_box" style="width:{{size}}px;height:{{size}}px">
<canvas class="circle_bg" canvas-id="{{draw}}bg" style="width:{{size}}px;height:{{size}}px"></canvas>
<canvas class="circle_draw" canvas-id="{{draw}}" style="width:{{size}}px;height:{{size}}px"></canvas>
<text class='circle_txt'> {{txt}}% </text>
</view>
4. 在 js 文件中实现代码
js 文件中先要定义出空间的属性,设定默认值以及对应的方法,以便于在参数被修改后直接更新画图。
Component({
options: {
multipleSlots: true // 在组件定义时的选项中启用多slot支持
},
properties: { //定义属性,对外可以被调用和设置的属性
draw: {//画板元素名称id
type: String,
value: 'draw',
observer:function(newVal,oldVal,change){
console.log(newVal, oldVal, change);
this.onreset(); //数值变化是所有重绘
}
},
per:{ //百分比 通过此值转换成step
type: String,
value: '0',
observer:function(newVal,oldVal,change){
console.log(newVal, oldVal, change);
this.onreset();//数值变化是所有重绘
}
},
r:{//半径
type: String,
value: '50',
observer:function(newVal,oldVal,change){
console.log(newVal, oldVal, change);
this.onreset();//数值变化是所有重绘
}
}
},
其中,observer 函数会在组件的属性发生变化时被调用,调用后会打印一些调试信息,最终调用的是 onreset 函数。
接下来看看 onreset 函数的内容:(这个在生命周期函数中也需要调用来刷新界面)
onreset: function () {
const _this = this;
//获取屏幕宽度
wx.getSystemInfo({
success: function (res) {
_this.setData({
screenWidth: res.windowWidth
});
},
});
//初始化
const el = _this.data.draw; //画板元素
const per = _this.data.per; //圆形进度
const r = Number(_this.data.r); //圆形半径
_this.setData({
step: (2 * Number(_this.data.per)) / 100, //这里将 0-100 转换为 0-2
txt: _this.data.per
});
//获取屏幕宽度(并把真正的半径px转成rpx)
let rpx = (_this.data.screenWidth / 750) * r; //真正的半径
//计算出画板大小
this.setData({
size: rpx * 2 //实际窗口的大小
});
const w = 10;//圆形的宽度
//组件入口,调用下面即可绘制 背景圆环和彩色圆环。
_this.drawCircleBg(el + 'bg', rpx, w);//绘制 背景圆环
_this.drawCircle(el, rpx, w, _this.data.step);//绘制 彩色圆环
}
onreset 函数主要功能是根据组件属性的新值来重新绘制 canvas,首先获取屏幕宽度,以便于计算相对的组件大小,从而适应更多种类的屏幕。
最后将参数进行转换,比如百分比转换成 0-2 之间的浮点数等。
最终设定圆形的宽度后调用 drawCircle 和 drawCircleBg 两个函数来分别绘制前景图和背景图。
背景图的绘制只是绘制一个灰色的圆环,代码相对简单,这里使用了最新的 canvas API 接口:
drawCircleBg: function (el, r, w) {
const query = wx.createSelectorQuery().in(this);
//wx.createSelectorQuery()
query.select('#' + el) // 在 WXML 中填入的 id
.fields({ node: true, size: true })
.exec((res) => {
const canvas = res[0].node;
const ctx = canvas.getContext('2d');
// Canvas 画布的实际绘制宽高
const width = res[0].width
const height = res[0].height
// 初始化画布大小
const dpr = wx.getWindowInfo().pixelRatio
canvas.width = width * dpr
canvas.height = height * dpr
ctx.scale(dpr, dpr)
//绘制前清空画布,原点 + 长和宽,这里画圆,因此清零 2r 的方块
ctx.clearRect(0, 0, 2 * r, 2 * r);
ctx.lineWidth = w; // 设置圆环的宽度
ctx.strokeStyle = '#E5E5E5'; // 设置圆环的颜色
ctx.lineCap = 'round'; // 设置圆环端点的形状
ctx.beginPath(); //开始一个新的路径
// r,r为原点,r-w 为半径,从 0 弧度到 2pi 弧度,顺时针(false)画弧度。
ctx.arc(r, r, r - w, 0, 2 * Math.PI, false); //设定路径
ctx.stroke();//对当前路径进行描边,真正的画
}); // */
},
这里一下几点注意:
- 查找组件要是用‘#’开头 ,这是新的属性定义的。
- query = wx.createSelectorQuery().in(this); 这里要是用 in(this)确保在 ready 期间可以找到组件。
- 一定要初始化画布大小,否则按照默认大小画出来的会变形。
- canvas 的一些方法变成了属性,比如原来的 SetLineWidth 变成了 lineWidth,注意大小写。
最后我们看一下前景图的绘制
drawCircle: function (el, r, w, step) {
const query = wx.createSelectorQuery().in(this);
//wx.createSelectorQuery()
query.select('#' + el) // 在 WXML 中填入的 id
.fields({ node: true, size: true })
.exec((res) => {
const canvas = res[0].node;
const context = canvas.getContext('2d');
// Canvas 画布的实际绘制宽高
const width = res[0].width
const height = res[0].height
// 初始化画布大小
const dpr = wx.getWindowInfo().pixelRatio
canvas.width = width * dpr
canvas.height = height * dpr
context.scale(dpr, dpr)
context.clearRect(0, 0, 2 * r, 2 * r);
// 设置渐变
var gradient = context.createLinearGradient(2 * r,2 * r, 0,0);
gradient.addColorStop("0", "#2661DD");
gradient.addColorStop("0.5", "#40ED94");
gradient.addColorStop("1.0", "#5956CC");
context.lineWidth = w; // 设置现线的宽度
context.strokeStyle = gradient; //设置颜色为渐变
context.lineCap = 'round'; //设置端点形状
context.beginPath();//开始一个新的路径
// step 从0到2为一周,注意 canvas 的坐标方向,从-90°划到正的 270°
context.arc(r, r, r - w, -Math.PI / 2, step * Math.PI - Math.PI / 2, false);
//context.stroke(); //对当前路径进行描边
step ? context.stroke() : ''; //当step为空的时候不画(0%)
})// */
},
前景图的绘制区别于背景图,它使用了渐变颜色,同时我们会根据参数中的 step 来进行特定角度的弧线的绘制。