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!