业务需求
从服务端拉取数据(如生成商品海报)的时候,一些情况下需要以圆环的形式展示进度,提升用户的等待体验。
需求分析
- 客户端发起ajax请求,且无法获取真实的进度。因此需要与服务端沟通确认生成海报请求大概所需的时间,如5s。
- 在发起请求时就开始显示进度圆环,在5s内达到95%(可自定义,须小于100%),因为此时有可能还未获取到请求结果数据。
- 定义目标进度值为95
- 可用定时器
setTimeout实现+1,时间间隔为:Math.ceil(5000/95),每次+1前检查进度是否到达目标进度值,达到后清空定时器timer。如未达到,就继续循环定时+1。
- 请求返回结果,需尽快(如200ms)更新进度条至100%。此时前面的进度条有两种状况:
- 前面的进度已到达95%,
timer已清空,此时需重新创建定时器setTimeout实现+1,在200ms内达到100%; - 前面进度未达到95%,
timer仍在运行,此时可选择清空定时器,重新再创建定时器setTimeout实现+1。或者 更新目标进度值为100%,时间间隔更新为Math.ceil(200/(100-currentProgressVal))。
- 前面的进度已到达95%,
- 圆环的css展示。这次需求运行在微信小程序端,使用了css3中的锥形渐变
conic-gradient,目前没有发现不兼容的情况。如果运行环境要考虑兼容性,可使用SVG方案 或border-radius,border进行模拟实现。
代码实现
.circle-progress{
width: 100rpx;
height:100rpx;
overflow:hidden;
border-radius: 50%;
background: conic-gradient(#666 80%,#b1b1b1 80% 100%);
}
.circle-progress:before{
content: '';
display: block;
width: 80rpx;
height:80rpx;
background:#fff;
border-radius: 50%;
margin: 10rpx auto;
}
<!-- progressVal 为当前进度值 -->
<view
class="circle-progress"
style="background: conic-gradient(#666 {{progressVal}}%,#b1b1b1 {{progressVal}}% 100%);"
></view>
js实现中只摘取了主要逻辑部分
//更新进度
updateProgress (options = {}) {
const {
to, // 目标进度值(int)
duration // 达到目标进度所需总时间(毫秒)
} = options;
const {progressVal } = this.data;
const progressRest = Math.ceil(duration / (to - progressNum)); //从当前进度值到目标进度每次加1的时间间隔
//更新data中目标进度值,rest间隔,以及进度完成回调
this.setData({
targetProgressNum: to,
progressRest
});
const update = () => {
this.progressTimer = setTimeout(() => {
if(this.data.progressVal < this.data.targetProgressNum) {
this.setData({
progressVal: this.data.progressVal + 1
}, update)
} else {
this.clearProgressTimer();
if(this.data.progressVal == 100) {
this.progressFinished();
}
}
}, this.data.progressRest)
};
if (!this.progressTimer) {
update()
}
},
//获取服务端数据
getServerData (e) {
// 防止重复点击
if(this.data.loadingPost) { return; }
let resData; //请求结果
// 显示loading,并更新进度条
this.setData({
loadingPost: true,
postError: false,
progressVal: 0
}, () => {
this.updateProgress({
to: 95,
duration: 5000 // 预估数据请求时间(毫秒)
})
});
wx.request({
url: '/api/serverUrl',
success (res) {
if(res.code == 0){
resData = res.data;
}
},
complete () {
//进度条完成后的回调
this.progressFinished = () => {
if(resData) {
this.setData({
loadingPost: false,
postData: resData || {},
postError: false
});
} else {
this.setData({
loadingPost: false,
postError: true
});
}
};
// 获取到服务端数据后,继续更新进度条至100%,完成后展示结果数据
this.updateProgress({
to: 100,
duration: 200
});
}
});
}