vue/react圆环进度条-编程思维

数据展示,一直是各行各业乐此不疲的需求,具体到前端开发行业,则是各种各种图表数据展示,各种表格数据展示,烦不胜烦(繁不胜繁)!
前几天刚做了折线图、柱状图、饼状图之类的图表数据展示效果,今天又碰到了类似圆环进度条的展示效果。天天跟数据打交道,天天跟接口打交道,项目做了不少,菜逼还是菜逼,都是泪啊!
其实说白了,是自己对canvas不熟,对CSS3不熟,所以就找了一个现成的轮子:

<template>
  <div class="content" ref="box">
    <svg style="transform: rotate(-90deg)" :width="width" :height="width" xmlns="http://www.w3.org/2000/svg">
      <circle :r="(width-radius)/2"
        :cy="width/2"
        :cx="width/2"
        :stroke-width="radius"
        :stroke="backgroundColor"
        fill="none"
      />
      <circle ref="$bar"
        :r="(width-radius)/2"
        :cy="width/2"
        :cx="width/2"
        :stroke="barColor"
        :stroke-width="radius"
        :stroke-linecap="isRound ? 'round' : 'square'"
        :stroke-dasharray="(width-radius)*3.14"
        :stroke-dashoffset="isAnimation ? (width-radius) * 3.14 : (width - radius) * 3.14 * (100 - progress) / 100"
        fill="none"
      />
    </svg>
    <div class="center_text" :style="{color, fontSize}">
      <p v-if="!$slots.default" class="title">{{progress}}%</p>
      <slot></slot>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    radius: {
      type: [Number, String],
      default: 20
    }, // 进度条厚度
    progress: {
      type: [Number, String],
      default: 20
    }, // 进度条百分比
    barColor: {
      type: String,
      default: "#1890ff"
    }, // 进度条颜色
    backgroundColor: {
      type: String,
      default: "rgba(0,0,0,0.3)"
    }, // 背景颜色
    isAnimation: {
      // 是否是动画效果
      type: Boolean,
      default: true
    },
    isRound: {
      // 是否是圆形画笔
      type: Boolean,
      default: true
    },
    id: {
      // 组件的id,多组件共存时使用
      type: [String, Number],
      default: 1
    },
    duration: {
      // 整个动画时长
      type: [String, Number],
      default: 1000
    },
    delay: {
      // 延迟多久执行
      type: [String, Number],
      default: 200
    },
    timeFunction: {
      // 动画缓动函数
      type: String,
      default: "cubic-bezier(0.99, 0.01, 0.22, 0.94)"
    },
    circleWidth: {
      //圆环宽度
      type: Number,
      default: 100,
    },
    color: {
      //文字颜色
      type: String,
      default: '#000'
    },
    fontSize: {
      //文字大小
      type: String,
      default: '18px'
    }
  },
  data() {
    return {
      width: this.circleWidth,
      idStr: `circle_progress_keyframes_${this.id}`
    };
  },
  beforeDestroy() {
    // 清除旧组件的样式标签
    document.getElementById(this.idStr) &&
    document.getElementById(this.idStr).remove();
    window.addEventListener(() => {});
  },
  mounted() {
    let self = this;
    this.setCircleWidth();
    this.setAnimation();
    // 此处不能使用window.onresize
    window.addEventListener(
      "resize",
      debounce(function() {
        self.setCircleWidth();
        self.setAnimation(self);
      }, 300)
    );
  },
  methods: {
    setCircleWidth() {
      let box = this.$refs.box;
      let width = box.clientWidth;
      let height = box.clientHeight;
      let cW = width > height ? height : width;
      this.width = cW;
    },
    setAnimation() {
      let self = this;
      if (self.isAnimation) {
        // 重复定义判断
        if (document.getElementById(self.idStr)) {
          console.warn("vue-circle-progress should not have same id style");
          document.getElementById(self.idStr).remove();
        }
        // 生成动画样式文件
        let style = document.createElement("style");
        style.id = self.idStr;
        style.type = "text/css";
        style.innerHTML = `
      @keyframes circle_progress_keyframes_name_${self.id} {
      from {stroke-dashoffset: ${(self.width - self.radius) * 3.14}px;}
      to {stroke-dashoffset: ${((self.width - self.radius) *
        3.14 *
        (100 - self.progress)) /
        100}px;}}
      .circle_progress_bar${
        self.id
      } {animation: circle_progress_keyframes_name_${self.id} ${
          self.duration
        }ms ${self.delay}ms ${self.timeFunction} forwards;}`;
        // 添加新样式文件
        document.getElementsByTagName("head")[0].appendChild(style);
        // 往svg元素中添加动画class
        self.$refs.$bar.classList.add(`circle_progress_bar${self.id}`);
      }
    }
  }
};
</script>
<style scoped>
.content {height:100%;display:flex;justify-content:center;align-items: center;}
.center_text {position:absolute;}
</style>

使用方法:

<CircleProgress :id="'circle1'" :circleWidth="40" :radius="7" :progress="30" :isAnimation="true" :backgroundColor="'#E9E9E9'" :barColor="'#FF4F4F'" />
<CircleProgress :id="'circle2'" :circleWidth="40" :radius="7" :progress="50" :isAnimation="true" :backgroundColor="'#E9E9E9'" :barColor="'#FF902A'" />
<CircleProgress :id="'circle3'" :circleWidth="40" :radius="7" :progress="89" :isAnimation="true" :backgroundColor="'#E9E9E9'" :barColor="'#FFDB4F'" />
<CircleProgress :id="'circle4'" :circleWidth="40" :radius="7" :progress="25" :isAnimation="true" :backgroundColor="'#E9E9E9'" :barColor="'#B8D87E'" />

使用时需要注意一下,如果你的页面中同时使用了超过两个以上的这种圆环进度条,就需要给每个圆环进度条设置不同的id,否则,所有圆环最终展示的数据都会是最后一个圆环的数据。

代码中有一个防抖动的函数,这里就贴一下这个函数:

function debounce(func, wait, immediate) {
  let timeout, args, context, timestamp, result

  const later = function () {
    // 据上一次触发时间间隔
    const last = +new Date() - timestamp

    // 上次被包装函数被调用时间间隔last小于设定时间间隔wait
    if (last < wait && last > 0) {
      timeout = setTimeout(later, wait - last)
    } else {
      timeout = null
      // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
      if (!immediate) {
        result = func.apply(context, args)
        if (!timeout) context = args = null
      }
    }
  }

本文参考的是npm上的一个圆环进度条的插件vue-circleprogressbar,之所以没有在项目中直接安装并使用这个插件,是因为这个插件有点不太符合我们开发的需求,比如这个插件不能设置圆环的宽度,不能设置文字的颜色,不能设置文字的大小,再比如这个插件仅仅为了防抖而依赖了lodash(这个lodash的体积还是很大的)。

至于这个组件在react中的使用,按照react的生命周期,或者react hooks的语法,或者dva模式的语法,稍微改巴改巴就可以使用了,很简单,就不再展开了。

版权声明:本文版权归作者所有,遵循 CC 4.0 BY-SA 许可协议, 转载请注明原文链接
https://www.cnblogs.com/tnnyang/p/11655317.html

vue将接口返回的日期实时转换为几分钟前、几小时前、几天前-编程思维

项目开发中,各种需求都会遇到,有些需求很合理,也好实现,有些需求不能说不合理,就是太麻烦,就比如类似标题所描述这种的需求,你不能说它是不合理的需求,因为很多论坛或微博、朋友圈、QQ空间之类的这种效果还是很常见的,能让我们一眼就看到这些信息是什么时候发的,而且这些时间基本都是实时自动更新的,给人的感觉很友好。 而在我们公

vue根据选择的月份动态展示当前月份的每一天并展示每一天所对应的星期几-编程思维

我们在开发过程中所遇到的所有的奇奇怪怪的交互美其名曰用(奇)户(葩)体(需)验(求),而产品所谓的良好的交互效果,在我等开发人员眼里不值一提、不屑一顾、讨厌至极! 对于这样的需求,我通常都是: 但胳膊拧不过大腿,人微言轻的后果就是你没得选择,在如此寒冷的大约在冬季: 我等卑微的码农,依旧还得唱着“你我皆凡人,生在

自定义element父子不关联的穿梭树-编程思维

Element自身是有一个Transfer穿梭框组件的,这个组件是穿梭框结合checkbox复选框来实现的,功能比较单一,自己想实现这个功能也是很简单的,只是在项目开发中,项目排期紧,没有闲功夫来实现罢了,但这个组件只适合用来实现较为简单的左右数据添加删除的效果,复杂一点的树结构穿梭框就难实现多了,当然也有造好的轮子等

vue文字间歇无缝向上滚动-编程思维

公司的管理系统中有“文字间歇无缝向上滚动”的需求,现在这种需求基本在项目开发中已经消失了,没什么新颖的,但架不住公司高层喜欢这种玩意儿,所以,作为开发人员,即使你有一百个不乐意,谁让是人家给你发工资呢!!! 其实,这种用在vue项目中的需求跟原生js的实现方法基本一致,且实现的方法有多种,今天就单拎出来一种实现方法吧:

vue+element-ui之tree树形控件有关子节点和父节点之间的各种选中关系详解-编程思维

做后端管理系统,永远是最蛋疼、最复杂也最欠揍的事情,也永远是前端开发人员最苦逼、最无奈也最尿性的时刻。蛋疼的是需求变幻无穷,如同二师兄的三十六般变化;复杂的是开发难度寸步难行,如同蜀道难,难于上青天;欠揍的是产品随心所欲、为所欲为,如同村霸横行乡里、只手遮天;苦逼的是前端苦不堪言,如同哑巴吃黄连,有苦说不出;无奈的是前

关于值班表的前端显示-编程思维

  有这么一张图,如下,有没有想起它是什么呢? 遇到实际问题: 比如值班人员具体值班时间显示,领导说,我要很直接的看出,哪些同学在哪个时间段的值班状态。 嗯是的,就是这么一句话。需求也就是这么一句话,剩下的自己发挥。 思考: 1.值班时间是一天24小时,不同的人轮流换班工作,也就是在这一天里,在某个时间段,人员是流动

c# 控制台console进度条-编程思维

1 说明 笔者大多数的开发在 Linux 下,多处用到进度条的场景,但又无需用到图形化界面,所以就想着弄个 console 下的进度条显示。 2 步骤 清行显示 //清行处理操作 int currentLineCursor = Console.CursorTop;//记录当前光标位置 Console.SetCurs