An Example of Vue Realizing Ring Progress Bar

  • 2021-10-25 06:01:45
  • OfStack

Data display, 1 is the demand of all walks of life. Specific to the front-end development industry, it is all kinds of chart data display and all kinds of table data display, which is annoying (numerous and complicated)!
A few days ago, I just made chart data display effects such as line chart, bar chart and pie chart, and today I met the display effect similar to ring progress bar. Dealing with data every day, dealing with interfaces every day, doing a lot of projects, forcing food or forcing food, all tears!
In fact, to put it bluntly, I am unfamiliar with canvas and CSS3, so I found a ready-made wheel:


<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 bar thickness 
 progress: {
  type: [Number, String],
  default: 20
 }, //  Percentage of progress bar 
 barColor: {
  type: String,
  default: "#1890ff"
 }, //  Progress bar color 
 backgroundColor: {
  type: String,
  default: "rgba(0,0,0,0.3)"
 }, //  Background color 
 isAnimation: {
  //  Whether it is an animation effect 
  type: Boolean,
  default: true
 },
 isRound: {
  //  Is it a round brush 
  type: Boolean,
  default: true
 },
 id: {
  //  Component's id Use when multiple components coexist 
  type: [String, Number],
  default: 1
 },
 duration: {
  //  The whole animation duration 
  type: [String, Number],
  default: 1000
 },
 delay: {
  //  How long is the execution delayed 
  type: [String, Number],
  default: 200
 },
 timeFunction: {
  //  Animation slow motion function 
  type: String,
  default: "cubic-bezier(0.99, 0.01, 0.22, 0.94)"
 },
 circleWidth: {
  // Ring width 
  type: Number,
  default: 100,
 },
 color: {
  // Text color 
  type: String,
  default: '#000'
 },
 fontSize: {
  // Text size 
  type: String,
  default: '18px'
 }
 },
 data() {
 return {
  width: this.circleWidth,
  idStr: `circle_progress_keyframes_${this.id}`
 };
 },
 beforeDestroy() {
 //  Clear the style labels of old components 
 document.getElementById(this.idStr) &&
 document.getElementById(this.idStr).remove();
 window.addEventListener(() => {});
 },
 mounted() {
 let self = this;
 this.setCircleWidth();
 this.setAnimation();
 //  Cannot be used here 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) {
  //  Repeated definition judgment 
  if (document.getElementById(self.idStr)) {
   console.warn("vue-circle-progress should not have same id style");
   document.getElementById(self.idStr).remove();
  }
  //  Generate an animation style file 
  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;}`;
  //  Add a new style file 
  document.getElementsByTagName("head")[0].appendChild(style);
  //  To svg Add animation to the element 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>

Usage:


<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'" />

Note 1 when using. If you use more than two of these ring progress bars at the same time in your page, you need to set a different id for each ring progress bar. Otherwise, the data displayed by all rings will be the data of the last ring.

There is a function of anti-jitter in the code, so stick this function here:


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

 const later = function () {
 //  According to the 1 Secondary trigger time interval 
 const last = +new Date() - timestamp

 //  Time interval between last time the wrapped function was called last Less than the set time interval wait
 if (last < wait && last > 0) {
  timeout = setTimeout(later, wait - last)
 } else {
  timeout = null
  //  If set to immediate===true Because the starting boundary has already been called, there is no need to call here 
  if (!immediate) {
  result = func.apply(context, args)
  if (!timeout) context = args = null
  }
 }
 }

This article refers to the plug-in of a ring progress bar on npm vue-circleprogressbar You did not install and use this plug-in directly in your project, It is because this plug-in does not meet the needs of our development. For example, this plug-in can't set the width of the ring, the color of the text, and the size of the text. For example, this plug-in only relies on lodash for anti-shake (the volume of this lodash is still very large).

As for the use of this component in react, according to the life cycle of react, or the syntax of react hooks, or the syntax of dva mode, it can be used by slightly changing it, which is very simple and will not be expanded.

Author: Xiao Bad

Source: http://tnnyang.cnblogs.com

The above is the details of the example of Vue implementation of the ring progress bar. For more information about Vue implementation of the ring progress bar, please pay attention to other related articles on this site!


Related articles: