vue 实现水波进度条和环形进度条效果

2 篇文章 0 订阅
2 篇文章 0 订阅

一. 水波进度条具体效果

请添加图片描述
先说下具体实现思路
首选使用div画出一个圆, 把外轮廓画出来
下面是是html结构

在这里插入图片描述

颜色可以根据自己的需要设置, 具体波浪实现思路是, 画一个带有圆角的div, 定位到这个原型的上面,然后使用css动画来进行无限旋转即可
在这里插入图片描述

然后贴上全部代码

<!-- eslint-disable vue/multi-word-component-names -->
<template>
  <div
    class="wave_content"
    :style="{height: height + 'px', background: styles.compConfig.type === 'dark' ? '#2f3d65' : '#f5f5f5'}"
  >
    <div class="wave_progress">
      <div
        class="wave"
        :style="{background: color}"
      >
        <div id="number_contnet">
          <div> {{ percent }} </div>
          <div>{{ title }}</div>
        </div>
      </div>
      <div
        :id="'wave_mask' + index"
        class="wave_mask"
        :style="{top: 100 - percentage === 0 ? (-90000 + '%') : 100 - percentage + '%'}"
      />
    </div>
    <div style="margin-top: 14px; display:flex; flex-wrap:wrap">
      <span
        v-show="showActual"
        style="font-weight: 500;margin-right: 10px"
      >
        实际: {{ actual }}
      </span>
      <span
        v-show="showObjective"
        style="font-weight: 500"
      >
        目标: {{ objective }}
      </span>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    data: {
      type: Object,
      default: () => {}
    },
    height: { // 可以动态设置高度
      type: [Number, String],
      default: 230
    },
    index: { // 使用v-for渲染时 index要唯一
      type: [String, Number],
      default: 0
    },
    percentage: { // 进度为 0 - 100 
      type: [Number],
      default: 0
    },
    percent: { // 自己计算的百分比
      type: [Number, String],
      default: 0
    },
    title: {
      type: String,
      default: ''
    },
    actual: { // 实际值
      type: String,
      default: ''
    },
    objective: { // 目标值
      type: String,
      default: ''
    },
    showActual: {
      type: Boolean,
      default: true
    },
    showObjective: {
      type: Boolean,
      default: true
    },
    color: { // 背景颜色
      type: String,
      default: ''
    }
  },
  data() {
    return {
      styles: this.data.styles,
    };
  },
  mounted() {
  },
  destroyed() {
  },
  methods: {
  }
};
</script>

<style lang="less" scoped>
@keyframes spin {
  50% {
    transform: translate(-50%, -101%) rotate(500deg);
  }
  100% {
    transform: translate(-50%, -101%) rotate(1000deg);
  }
}
.wave_content {
  width: 100%;
  display: flex;
  justify-content: center;
  flex-direction: column;
  align-items: center;
  padding: 0 10px;
  box-sizing: border-box;
}
.wave_progress {
  width: 140px;
  height: 140px;
  border-radius: 50%;
  background: #ffffff;
  overflow: hidden;
  position: relative;
  border: 3px solid #e4e7ef;
  padding: 4px;
  box-sizing: border-box;
  .wave {
    position: relative;
    width: 100%;
    height: 100%;
    // background-image: linear-gradient(-180deg, #07c2b7 13%, #07c2b7 100%);
    border-radius: 50%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
  }
  .wave_mask {
    position: absolute;
    width: 200%;
    height: 200%;
    top: 0;
    left: 50%;
    border-radius: 40%;
    transform: translate(-50%, -101%) rotate(0);
    animation-name: spin;
    animation-timing-function: linear;
    animation-delay: 0s;
    animation-duration: 16s;
    animation-iteration-count: infinite;
    z-index: 20;
    background-color: #f5f5f5;
  }
}
#number_contnet {
  position: absolute;
  color: #000;
  font-size: 16px;
  font-weight: bold;
  text-align: center;
  z-index: 100;
}
</style>

二. 环形进度条

实现效果
在这里插入图片描述

实现思路是使用echarts的pie扇形图来绘制
主要是设置radius: [‘75%’, ‘90%’],来实现镂空效果

贴上代码

<!-- eslint-disable vue/multi-word-component-names -->
<template>
  <div
    class="percentloop"
    :style="{height: height + 'px',background: styles.compConfig.type === 'dark' ? '#2f3d65' : '#f5f5f5'}"
  >
    <div
      :id="forId(index)"
      :ref="forId(index)"
      :key="index"
      style="height: 140px;width: 140px"
      class="chart-box"
    />
    <div style="margin-top: 14px; display:flex; flex-wrap:wrap">
      <span
        v-show="showActual"
        style="font-weight: 500;margin-right: 10px"
      >
        实际: {{ actual }}
      </span>
      <span
        v-show="showObjective"
        style="font-weight: 500"
      >
        目标: {{ objective }}
      </span>
    </div>
  </div>
</template>
 
<script>
// import echarts from 'src/utils/echartsLoader.js'; 
import echarts from 'echarts';

export default {
  props: {
    data: {
      type: Object,
      default: () => {}
    },
    height: {
      type: [Number, String],
      default: 230
    },
    index: { // 使用时要保证index 唯一
      type: [String, Number],
      default: 0
    },
    percentage: { // 进度不必是0-100,值随意, 因为下面有处理
      type: [Number],
      default: 0
    },
    percent: { // 百分比
      type: [Number, String],
      default: 0
    },
    title: {
      type: String,
      default: ''
    },
    actual: { // 实际值
      type: String,
      default: ''
    },
    objective: { // 目标值
      type: String,
      default: ''
    },
    showActual: {
      type: Boolean,
      default: true
    },
    showObjective: {
      type: Boolean,
      default: true
    },
    color: {
      type: String,
      default: ''
    },
  },
  data () {
    return {
      styles: this.data.styles
    };
  },
  watch: {
    percent: { // 百分比变化重新绘制, 可根据业务自行修改要监听的数据
      handler() {
        this.$nextTick(() => {
          this.init();
        });
      }
    },
    color: { // 颜色变化重新绘制
      handler() {
        this.$nextTick(() => {
          this.init();
        });
      }
    },
  },
  mounted() {
    this.init(); // 渲染完成后绘制
  },
  methods: {
    handlerNumber(num) {  // 函数可处理带% 和千分位的数据, 转化成number给echarts使用, 因为echarts 只能接收number 类型的数据
      let strNum = JSON.parse(JSON.stringify(num));
      if (strNum.includes(',')) {
        strNum = strNum.split(',').reduce((pre, item) => {
          return pre += item;
        }, '');
      }
      if (strNum.includes('%')) {
        strNum = strNum.split('%')[0] / 100;
      }
      return +strNum;
    },
    init() {
      const actual = this.handlerNumber(this.actual);
      const objective = this.handlerNumber(this.objective);
      // console.log('还原的数', actual, objective);
      // const dom = this.$refs[this.forId(this.index)];
      const dom = document.getElementById(this.forId(this.index));
      const chart = echarts.init(dom);
      // 使用镂空饼图模拟进度条
      chart.setOption({
        color: [this.color, '#dbdbdb'],
        tooltip: {
          show: false,
          trigger: 'item',
          formatter: '{b} : {c} ({d}%)',
        },
        legend: {
          show: false,
        },
        series: [
          {
            name: '详情',
            type: 'pie',
            radius: ['75%', '90%'],
            hoverAnimation: false,
            label: {
              show: true,
              position: 'center',
              normal: {
                show: true,
                position: 'center',
                formatter: () => {
                  const str = `${this.percent}\n${this.title}`; // 图中间内容 \n 进行换行
                  return str;
                },
                rich: {
                  b: {
                    fontSize: 20,
                    color: 'green',
                    fontWeight: 'bold',
                  },
                  c: {
                    fontSize: 13,
                    color: 'skyblue',
                    lineHeight: 30,
                  },
                },
                textStyle: {
                  fontSize: 16,
                  fontWeight: 600,
                  color: '#000',
                },
              },
            },
            data: [
              {
                value: actual, // 实际值
              },
              {
                // 如果实际值为0 则直接用目标值的进度沾满整个进度条
                value: actual !== 0 ? objective - actual < 0 ? 0 : objective - actual : 100, // 目标值
              }
            ],
            silent: true,
            // emphasis: {
            //   itemStyle: {
            //     shadowBlur: 10,
            //     shadowOffsetX: 0,
            //     shadowColor: 'rgba(0, 0, 0, 0.5)',
            //   },
            // },
          },
        ],
      });

    },
    forId(index) { // 主要是为了创建唯一id, 可根据业务自行调整
      return this.data.i + index;
    },
  }
};
</script>
 
<style scoped lang="less">
.percentloop {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: #f5f5f5;
}
</style>
  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值