微信小程序全栈开发实践 第二章 微信小程序组件介绍及使用 -- 2.2 progress组件,以及如何自定义实现一个环形进度条?

在前端网络操作是异步的,一般都需要一个进度条,在很多应用中,我们经常可以看到环形进度条。
但是小程序原生的progress组件,是一个从左到右的方形进度条,那么我们可不可以自实现一个环形进度条呢?

一、原生实例代码

在这里插入图片描述
可自定义的属性有关于颜色值的,颜色的使用需要符合微信小程序设计规范

https://developers.weixin.qq.com/miniprogram/design/

在这里插入图片描述

index.wxml

<!-- 单击模拟网络异步 -->
<view class="gap" >代码示例,单击模拟网络异步</view>

<progress show-info bindtap="onTapProgressBar"
stroke-width="2"  percent="{{percentValue}}"    
backgroundColor="#f2f2f2" active-mode="forwards"
active bindactiveend="onProgressActiveEnd" />

index.js

Page({
  data: {
    percentValue: 0
  },
  onProgressActiveEnd(e){
    console.log(e)
  },
  onTapProgressBar(e) {
    console.log(e)
    let progress = this.data.percentValue
    if (progress < 100) {
      progress += 5
      this.setData({
        percentValue: Math.min(100, progress)
      })
    }
  }
})

二、相关问题

2.1 如何实现一个下载文件并显示动态进度条的功能?

文档中percent这个属性必须设置固定的值,例如80,但是进度是一直变化的,该如何实现动态进度条呢?
我们前面的示例代码基本就可以满足这个需求了。当启用active,并将active-mode设置为forwards后,动画就会随下载进度动起来。
通过文件下载的总大小和已完成大小,可以实时计算出percent数值,需要注意的是percent属性是动态绑定的,
每次变化后我们需要显式使用setData触发视图更新,不然动画是不动的

2.2 progress 已产生的进度条如何设置圆角?
<progress border-radius="5"   percent="20" show-info />

在这里插入图片描述

2.3 已经加载完的进度条 progress,如何点击某个按钮,让它重新加载呢?

有人说开启active动画后,当进度条完成以后,直接在设置一下percent属性的值就可以实现。
但是这个方法行不通,直接将percent再设置为100或其他数值,并不能让动画重新播放。

有人设想改变两次percent,借助nextTick或延时定时器,分别在两个渲染周期设置。
nextTick是基础库2.2.3版本以上支持的,所以这位作者用了wx.canIUse这个接口,判断能不能使用这个API,
如果不能使用,则改用setTimeout设置一个延时定时器,先将percent的数值设置为0,
过了一个渲染周期或延时17毫秒再设置一次,这样就可以得到动画重新播放的效果。

this.setData({ percentValue: 0 });
if (wx.canIUse('nextTick')) {
	 wx.nextTick(() => {
	 	this.setData({ percentValue: 100 });
	 });
} else {
	 setTimeout(() => {
	 	this.setData({ percentValue: 100 });
	 }, 17)
}

其实还有更简单的方法,每一次setData在底层都需要调用evaluateJavascript这个底层函数,
这个函数用于逻辑层与视图层之间的通讯,它的执行本来就需要时间,因此,直接调用两次setData,也可以达到同样的效果。
连续两次调用setData可能看起来不是那么优雅,但是有时候看起来不那么优雅的代码,可能才是最简单和有效的代码。

onTapReloadBtn(e){
	 this.setData({percentValue:0})
	 this.setData({percentValue:50})
}
2.4 能否实现一个圆环形进度条呢?

用CSS绘制动画比较麻烦,可以用Canvas绘制,使用Component创建一个自定义组件,例如名字就叫circle-progress,
在这个组件的WXML代码里放置一个Canvas组件,并给他设置一个ID,这个ID为runCanvas的Canvas组件,它用于绘制上面绿色的圆,
灰色的圆圈是由一个灰色的底圆bigCircle,加一个白色的稍微小一点的圆littleCircle组合出来的,它是有CSS样式实现出来的,
在自定义组件中,通过一个percent的属性用于标识进度,这个属性与官方的progress组件具有相同的名称,这方便我们迁移代码和减小记忆负担。
observer用于自动监听属性变化,当进度增加时,调用draw函数绘制新增的绿色进度条,
在draw函数及后续调用的函数中,计算出需要绘制的弧度,及使用Canvas的弧度绘制Api arc进行绘制是实现环形效果的关键。

circle-progress是一个独立的组件,在使用时,
需要先在JSON配置中声明对组件的引用,circle-progress是声明的名称。
声明后在WXML中就可以把它当做标签使用了。
在button触发的JSON函数中,模拟网络变化改变进度值,就可以看到动画效果了。

实现自定义组件并不复杂,但有两点需要注意,
第一点,在自定义组件中使用wx.createCanvasContent创建画布的上下文绘制对象时,
需要在第二个参数处传递this对象,这样才是在组件中查找画布,不然只是在主页面中查找。
第二点,使用wx.createSelectorQuery创建的对象的select方法,以ID查找组件对象时,
如果在自定义组件中,必须在查找前先调用一下它的in方法,把this对象传递进去,不然组件是查找不到的。
默认组件查询也仅是在主页面中查找,不会涉及主页面中的子组件。

circle-progress组件

components/circle-progress/index.wxml

<view class='canvasBox'>
<!-- 外部灰色的圆  -->
  <view class='bigCircle'></view>
<!-- 内部白色的圆 -->
  <view class='littleCircle'></view>
  <canvas canvas-id="runCanvas" id="runCanvas" class='canvas'></canvas>
</view>

components/circle-progress/index.wxss

.canvasBox{
  height: 500rpx;
  position: relative;
  background-color: white;
}
/* 外部灰色的圆 */
.bigCircle{
  width: 420rpx;
  height: 420rpx;
  border-radius: 50%;
  position: absolute;
  top:0;
  bottom: 0;
  left: 0;
  right: 0;
  margin: auto auto;
  background-color: #f2f2f2;
}
/* 内部白色的圆 */
.littleCircle{
  width: 350rpx;
  height: 350rpx;
  border-radius: 50%;
  position: absolute;
  top:0;
  bottom: 0;
  left: 0;
  right: 0;
  margin: auto auto;
  background-color: white;
}
.canvas{
  width: 420rpx;
  height: 420rpx;
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  right: 0;
  margin: auto auto;
  z-index: 99;
}

components/circle-progress/index.js

Component({
  runTimerid:0,
  behaviors: [],
  properties: {
    percent: {
      type: Number,
      value: 0,
      observer: function (newVal, oldVal) {
        this.draw(newVal);
      }
    },
  },
  data: {
    percentage: '', //百分比
    animTime: '', // 动画执行时间
  }, // 私有数据,可用于模版渲染

  lifetimes: {
    // 生命周期函数,可以为函数,或一个在methods段中定义的方法名
    attached: function () { },
    moved: function () { },
    detached: function () { },
  },

  // 生命周期函数,可以为函数,或一个在methods段中定义的方法名
  attached: function () { }, // 此处attached的声明会被lifetimes字段中的声明覆盖

  pageLifetimes: {
    // 组件所在页面的生命周期函数
    show: function () { },
  },
  created() { },

  ready() {
    if (this.data.percent) this.draw(this.data.percent);
  },

  methods: {
    // 绘制圆形进度条方法
    run(c, w, h) {
      let that = this;
      var num = (2 * Math.PI / 100 * c) - 0.5 * Math.PI;
      that.ctx2.arc(w, h, w - 8, -0.5 * Math.PI, num)
      that.ctx2.setStrokeStyle("#09bb07");//绿色
      that.ctx2.setLineWidth("16");
      that.ctx2.setLineCap("butt");
      that.ctx2.stroke();

      that.ctx2.beginPath();
      that.ctx2.setFontSize(40); //注意不要加引号
      that.ctx2.setFillStyle("#b2b2b2");//浅灰色字体
      that.ctx2.setTextAlign("center");
      that.ctx2.setTextBaseline("middle");
      that.ctx2.fillText(c + "%", w, h);
      that.ctx2.draw();
    },
    // 动画效果实现
    canvasTap(start, end, time, w, h) {
      let that = this;
      start++;
      if (start > end) {
        return false;
      }
      that.run(start, w, h);
      
      that.runTimerid = setTimeout(function () {
        that.canvasTap(start, end, time, w, h);
      }, time);
    },

    draw(percent) {
      const id = 'runCanvas'
      const animTime = 500
      if (percent > 100) return
      if (!this.ctx2) {
        const ctx2 = wx.createCanvasContext(id, this)
        this.ctx2 = ctx2
      }

      let oldPercentValue = this.data.percentage
      this.setData({
        percentage: percent,
        animTime: animTime
      });
      var time = this.data.animTime / (this.data.percentage-oldPercentValue);

      const query = wx.createSelectorQuery().in(this)
      query.select('#' + id).boundingClientRect((res) => {
        var w = parseInt(res.width / 2);
        var h = parseInt(res.height / 2);
        if (this.runTimerid) clearTimeout(this.runTimerid)
        this.canvasTap(oldPercentValue, percent, time, w, h)
      }).exec()
    }
  }
})

在这里插入图片描述

使用
index.wxml

<!-- 7 环形进度条 -->
<view class="gap">环形进度条</view>
<circle-progress id="progress1" percent="{{percentValue}}" />
<button bindtap="drawProgress">redraw</button>

index.js

// 7环形进度条
drawProgress(){
	if (this.data.percentValue >= 100){
		this.setData({
			percentValue:0
		})
		  }
		this.setData({
			percentValue:this.data.percentValue+10
		 })
	}
}

index.json

{
  "usingComponents": {
    "circle-progress":"../../components/circle-progress/index",
  }
}
2.5 progress 右边进度条的百分比数字,它的颜色怎么设置?

有两个方法,
1.直接使用内联样式

 <progress percent="40" stroke-width="5" show-info style="color:red"/>

2.在本地样式文件中找到类样式的名称 ,然后在页面中重写

.wx-progress-info {
	color: red;
}

在这里插入图片描述

2.6 progress 组件右侧的百分比文字,与左边离得太近了,可否增加一个边距?
.wx-progress-info { 
	color: red;margin-left: 5px;
}
2.7 问题3中,为什么setTimeout设置的延时定时器要使用17毫秒

这是因为目前小程序1秒内最大渲染的帧数是60帧,每帧渲染约平均花费16.66毫秒,这是一个渲染周期最小的时间单位,17毫秒约等于这个数。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值