进度条/圆环需求分析与css实现

1,079 阅读2分钟

业务需求

从服务端拉取数据(如生成商品海报)的时候,一些情况下需要以圆环的形式展示进度,提升用户的等待体验。

需求分析

  1. 客户端发起ajax请求,且无法获取真实的进度。因此需要与服务端沟通确认生成海报请求大概所需的时间,如5s。
  2. 在发起请求时就开始显示进度圆环,在5s内达到95%(可自定义,须小于100%),因为此时有可能还未获取到请求结果数据。
    • 定义目标进度值为95
    • 可用定时器setTimeout实现+1,时间间隔为:Math.ceil(5000/95),每次+1前检查进度是否到达目标进度值,达到后清空定时器timer。如未达到,就继续循环定时+1。
  3. 请求返回结果,需尽快(如200ms)更新进度条至100%。此时前面的进度条有两种状况:
    • 前面的进度已到达95%, timer已清空,此时需重新创建定时器setTimeout实现+1,在200ms内达到100%;
    • 前面进度未达到95%,timer仍在运行,此时可选择清空定时器,重新再创建定时器setTimeout实现+1。或者 更新目标进度值为100%,时间间隔更新为Math.ceil(200/(100-currentProgressVal))
  4. 圆环的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
        });
    }
  });
 }