likes
comments
collection
share

基于el-progress 个性进度条(vue)

作者站长头像
站长
· 阅读数 24

前言

  • 最近做了个搜索页面,出结果大概3s吧,领导看了后觉得比较慢让优化,后端说搜索条件太多了,已经没法再快了,说让前端想想办法
  • 我一想前端也快不了啊,只能做到让用户感觉快了,之前是用的el-loading,那就修改为进度条吧
  • 就换成了el-progerss组件,结果领导看完说太粗糙了,让ui重新设计了一款,领导看了设计图很满意,我也很'满意',压力给到前端这边

开始

先看下效果图

基于el-progress 个性进度条(vue)

封装了一个Progress.vue组件

  • Progerss.vue
<template>
  <div class="progress">
    <el-progress
      :text-inside="true"
      :percentage="percentage"
      :class="progressClass"
      v-bind="$attrs"
      v-on="$listeners"
      :key="reRenderNum"
      style="width: 300px"
    ></el-progress>
    <span class="tip">
      搜索中
      <span class="dot-ani"></span>
    </span>
  </div>
</template>
<script>
export default {
  name: 'progress-bar',
  props: {
    progressClass: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      status: 0,
      speed: 100,
      minimum: 0.08,
      reRenderNum: 1, // 避免 status由 1-> 0 时,导致进度条倒退。这里直接重新渲染
      resetStatus: true // 避免 status 赋值为 0 时,直接改变进度条样式,需等待再次start进度条才会置空
    };
  },
  watch: {
    percentage(n) {
      this.$refs.iconRef && (this.$refs.iconRef.style.left = `${n}%`);
    }
  },
  computed: {
    percentage() {
      return parseInt(this.status * 100);
    }
  },
  methods: {
    // 外部调用使用,当无需使用假的进度条,直接设置 百分比
    setStatus(n) {
      this.status = n;
    },
    // 外部调用使用
    start() {
      if (this.resetStatus) {
        this.status = 0;
        this.resetStatus = false;
        this.reRenderNum++;
        this.progress(0);
      }
      const that = this;
      const work = function () {
        setTimeout(function () {
          if (that.resetStatus) return;
          that.mockIncrease();
          work();
        }, that.speed);
      };

      work();
    },
    // 外部调用使用
    done(force) {
      if (force || this.status) {
        this.mockIncrease(0.8 + 0.4 * Math.random());
        this.progress(1);
      }
    },
    // 向外部发送假的进度
    progress(n) {
      this.status = this.clamp(n, this.minimum, 1);
      // 当 status 为 1 时,进度条停止
      if (this.status === 1) {
        this.resetStatus = true;
      }
      this.$emit('progress', n);
    },
    mockIncrease(amount) {
      let n = this.status;
      // 如果直接调用 mockIncrease,那么会初始化 start
      if (!n) {
        this.start();
      } else if (n > 1) {
        return;
      } else {
        if (typeof amount !== 'number') {
          if (n >= 0 && n < 0.2) {
            amount = 0.1;
          } else if (n >= 0.2 && n < 0.5) {
            amount = 0.04;
          } else if (n >= 0.5 && n < 0.8) {
            amount = 0.02;
          } else if (n >= 0.8 && n < 0.99) {
            amount = 0.005;
          } else {
            amount = 0;
          }
        }
      }
      n = this.clamp(n + amount, 0, 0.994);
      this.progress(n);
    },
    // 保持进度 n 处于指定的状态(不会过小,也不会超出一定范围)
    clamp(n, min, max) {
      if (n < min) return min;
      if (n > max) return max;
      return n;
    }
  }
};
</script>
<style lang="less" scoped>
.progress {
  width: 100vw;
  height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: 24vh;
  position: absolute;
  left: 0;
  top: 0;
  background-color: #202020bb;
  z-index: 10;
  .tip {
    margin-top: 4px;
    color: #fff;
    font-size: 14px;
  }
  .dot-ani {
    display: inline-block;
    height: 12px;
    line-height: 12px;
    overflow: hidden;
  }
  .dot-ani::after {
    display: inline-table;
    white-space: pre;
    content: '\A.\A..\A...';
    animation: spin 2s steps(4) infinite;
  }
  @keyframes spin {
    to {
      -webkit-transform: translateY(-48px);
      transform: translateY(-48px);
    }
  }

  :deep(.el-progress-bar__outer) {
    border: 1px solid;
    overflow: unset;
  }
  :deep(.el-progress-bar__inner) {
    background-image: repeating-linear-gradient(
      -45deg,
      #4890ff 0%,
      #4890ff 1%,
      #7bbcff 1%,
      #7bbcff 2%
    );

    background-position: -2% 0;
    background-size: 150%;
    animation: slider 5s linear infinite;
  }
    // 这里利用了内置的进度文字滑动的效果
  :deep(.el-progress-bar__innerText) {
    display: block;
    width: 32px;
    height: 64px !important;
    float: right;
    background-image: url('../assets/images/progress-icon.png'); // 这个是对应的上面奔跑小人icon
    background-size: 32px 32px;
    background-repeat: no-repeat;
    background-position: 0 24px;
    margin: -14px -10px;
    position: absolute;
    top: -20px;
    right: 0;
    font-size: 18px;
  }
  @keyframes slider {
    100% {
      background-position: -100% 0;
    }
  }
}
</style>

引用

  • index.vue
<template>
<div>
    <button @click="goSearch">搜索</button>
    <ProgressBar ref="progressBarRef" :stroke-width="12" v-if="showProgress" />
</div>
</template>

<script>
export default {
   data(){
   return {
        showProgress: false
       }
   },
  methods: {
    startProgress() {
      this.showProgress = true;
      this.$nextTick(() => {
        this.$refs.progressBarRef.start();
      });
    },
    endProgress() {
      this.$refs.progressBarRef.done(true);
      this.showProgress = false;
    },
    // 这里调用
    goSearch(){
        this.startProgress();
        // 发送请求
        axios.get('***')
        .then()
        .finally(()=>{
        this.endProgress();
        })
    }
 }
}
</script>
<style scoped></style>

参考文章

转载自:https://juejin.cn/post/7362743871979274255
评论
请登录