Several Realizations of Linear Progress Bar

In large-screen visualization applications, the progress bar can intuitively show the progress of a certain indicator. There are many display forms of the progress bar, such as circular progress bar, linear progress bar, etc. The implementation plan of the circular progress bar can refer to the previous writing An article of SVG drawing a circular progress bar icon-default.png?t=M4ADhttps://blog.csdn.net/qq_40289557/article/details/123061211?spm=1001.2014.3001.5501

This chapter provides some personal insights and specific implementation solutions (with vue as the technology stack) for several implementation solutions of the linear progress bar. I hope to bring you a good effect. The display effect is as follows:

Click the small hand above the title to switch the display form:  

First, the code of the framework page is as follows, and the six progress bar components are introduced in sequence, and the personalized parameters are passed to the subcomponents by passing values ​​from the components:

<template>
  <div class="progress">
    <div class="svg-module">
      <div class="module-title">
        <span>背景渐变-1-</span>
        <span class="enable-click" @click="handleClick1">
          <i class="el-icon-right"></i>
          <i class="el-icon-thumb"></i>
        </span>
      </div>
      <div class="module-content">
        <bg-gradient :skew="skew1"></bg-gradient>
      </div>
    </div>
    <div class="svg-module">
      <div class="module-title">
        <span>盒子阴影-2-</span>
        <span class="enable-click" @click="handleClick2">
          <i class="el-icon-right"></i>
          <i class="el-icon-thumb"></i>
        </span>
      </div>
      <div class="module-content">
        <box-shadow :skew="skew2"></box-shadow>
      </div>
    </div>

    <div class="svg-module">
      <div class="module-title">条纹动画-3-</div>
      <div class="module-content">
        <stripe></stripe>
      </div>
    </div>

    <div class="svg-module">
      <div class="module-title">
        <span>条纹进度条-4-</span>
        <span class="enable-click" @click="handleClick3">
          <i class="el-icon-right"></i>
          <i class="el-icon-thumb"></i>
        </span>
      </div>
      <div class="module-content">
        <gap-line :skew="skew3"></gap-line>
      </div>
    </div>

    <div class="svg-module">
      <div class="module-title">
        <span>圆角进度条-5-</span>
        <span class="enable-click" @click="handleClick4">
          <i class="el-icon-right"></i>
          <i class="el-icon-thumb"></i>
        </span>
      </div>
      <div class="module-content">
        <gradient-bar :bg-color="bgColor" @click="handleClick4"></gradient-bar>
      </div>
    </div>

    <div class="svg-module">
      <div class="module-title">伪3D柱形进度条-6-</div>
      <div class="module-content">
        <line-circle></line-circle>
      </div>
    </div>
  </div>
</template>
<script>
import BgGradient from "./bggradient";
import BoxShadow from "./boxshadow";
import Stripe from "./stripe";
import GapLine from "./gapline";
import GradientBar from "./gradientbar";
import LineCircle from "./linecircle";
export default {
  name: "SVGLineIndex",
  components: {
    BgGradient,
    BoxShadow,
    Stripe,
    GapLine,
    GradientBar,
    LineCircle,
  },
  data() {
    return {
      skew1: 0,
      skew2: 0,
      skew3: -30,
      bgColor: "linear-gradient(to right, #0db7f2, #00de95)",
    };
  },
  methods: {
    handleClick1() {
      if (this.skew1 === 0) {
        this.skew1 = -30;
      } else {
        this.skew1 = 0;
      }
    },
    handleClick2() {
      if (this.skew2 === 0) {
        this.skew2 = -30;
      } else {
        this.skew2 = 0;
      }
    },
    handleClick3() {
      if (this.skew3 === 0) {
        this.skew3 = -30;
      } else {
        this.skew3 = 0;
      }
    },
    handleClick4() {
      if (this.bgColor === "#0db7f2") {
        this.bgColor = "linear-gradient(to right, #0db7f2, #00de95)";
      } else {
        this.bgColor = "#0db7f2";
      }
    },
  },
};
</script>
<style scoped>
.progress {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-around;
  align-content: space-around;
  width: 100%;
  height: 100%;
  background-color: #071727;
  overflow: hidden;
}
.svg-module {
  width: 30%;
  height: 46%;
}
.module-content {
  width: 100%;
  height: calc(100% - 30px);
  padding: 10px;
  border: 1px solid #064774;
  color: #fff;
  box-sizing: border-box;
}
.module-title {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 30px;
  line-height: 30px;
  font-size: 16px;
  text-align: center;
  color: #fff;
}
.module-title::before,
.module-title::after {
  content: "";
  position: absolute;
  z-index: -1;
  top: 0;
  width: 50%;
  height: 100%;
  background-image: linear-gradient(to bottom, #061223, #042c4c);
}
.module-title::before {
  left: 0;
  transform: skew(-45deg);
  transform-origin: left bottom;
}
.module-title::after {
  right: 0;
  transform: skew(45deg);
  transform-origin: right bottom;
}
.enable-click {
  cursor: pointer;
}
</style>

 1. First look at the implementation method of the first chart "Background Gradient -1-", which is implemented by superimposing the background layer + progress bar. Here, the feature of the background in the CSS property that can be tiled is used, combined with the linear gradient Various display effects can be achieved by using features such as and distortion. The specific implementation logic is as follows:

<!-- bggradient.vue -->
<template>
  <div class="container" :style="{ '--skew': skew + 'deg' }">
    <div class="type-box" v-for="(item, index) in dataList" :key="index">
      <div class="type-name">{
   
   { item.name }}</div>
      <div class="type-progress">
        <div class="type-progress-rect" :style="{ width: item.rate }"></div>
      </div>
      <div class="type-num">{
   
   { item.rate }}</div>
    </div>
  </div>
</template>
<script>
export default {
  name: "BgGradient",
  props: {
    skew: {
      type: Number,
      default: 0,
    },
  },
  data() {
    return {
      dataList: [
        { name: "濮阳县", value: 92.45, rate: "92.45%" },
        { name: "清丰县", value: 64.55, rate: "64.55%" },
        { name: "范县", value: 50.25, rate: "50.25%" },
        { name: "台前县", value: 25.38, rate: "25.38%" },
        { name: "南乐县", value: 10.04, rate: "10.04%" },
      ],
    };
  },
};
</script>
<style scoped>
.container {
  display: flex;
  width: 100%;
  height: 100%;
  justify-content: center;
  flex-wrap: wrap;
  align-content: space-between;
}

.type-box {
  display: flex;
  align-items: center;
  width: 100%;
  height: 40px;
}

.type-name {
  width: 60px;
  font-size: 16px;
  color: #fff;
}

.type-progress {
  position: relative;
  flex: 1;
  height: 20px;
  /* 核心代码 */
  background: linear-gradient(to right, #252d6e 50%, transparent 50%) 0 0/16px
    100% repeat-x;
  transform: skew(var(--skew));
}

.type-progress-rect {
  width: 100%;
  height: 100%;
  /* 核心代码 */
  background: linear-gradient(to right, #48ebf4 50%, transparent 50%) 0 0/16px
    100% repeat-x;
}

.type-num {
  width: 70px;
  font-size: 16px;
  color: #fff;
  text-align: right;
}
</style>

2. The second chart "Box Shadow-2-", the chart is composed of small grids with rounded corners, and there is a gradient effect from left to right. One of the easiest solutions for this type of chart is to use CSS properties The box-shadow attribute can achieve various display effects by setting the rounded corners, width, offset of the box shadow, and twist angle of the grid. The code is as follows:

<!-- boxshadow.vue -->
<template>
  <div class="container" :style="{ '--skew': skew + 'deg' }">
    <div class="type-box" v-for="(item, index) in dataList" :key="index">
      <div class="type-name">{
   
   { item.name }}</div>
      <div class="type-progress">
        <div class="progress" :style="{ width: item.rate }"></div>
      </div>
      <div class="type-num">{
   
   { item.rate }}</div>
    </div>
  </div>
</template>
<script>
export default {
  name: "BoxShadow",
  props: {
    skew: {
      type: Number,
      default: 0,
    },
  },
  data() {
    return {
      dataList: [
        { name: "濮阳县", value: 92.44, rate: "92.44%" },
        { name: "清丰县", value: 64.55, rate: "64.55%" },
        { name: "范县", value: 50.25, rate: "50.25%" },
        { name: "台前县", value: 25.38, rate: "25.38%" },
        { name: "南乐县", value: 10.04, rate: "10.04%" },
      ],
    };
  },
};
</script>
<style scoped>
.container {
  display: flex;
  width: 100%;
  height: 100%;
  justify-content: center;
  flex-wrap: wrap;
  align-content: space-between;
}
.type-box {
  display: flex;
  align-items: center;
  width: 100%;
  height: 40px;
}

.type-name {
  width: 60px;
  font-size: 16px;
  color: #fff;
}

.type-progress {
  position: relative;
  flex: 1;
  height: 30px;
  border: 1px solid #1a76e3;
  border-radius: 6px;
}

.type-num {
  width: 70px;
  font-size: 16px;
  color: #fff;
  text-align: center;
}

.progress {
  display: flex;
  align-items: center;
  height: 100%;
  box-shadow: 0 0 #176ce9;
  overflow: hidden;
  transform: skew(var(--skew));
}

.progress::before {
  content: "";
  display: block;
  margin-left: 6px;
  border-radius: 2px;
  width: 10px;
  height: 20px;
  background-color: #196fe8;
  /* 核心代码 */
  box-shadow: 14px 0 #1974e6, 28px 0 #1d80dd, 42px 0 #2086db, 56px 0 #2393d2,
    70px 0 #2598ce, 84px 0 #29a4c5, 98px 0 #2ba9c1, 112px 0 #2fafba,
    126px 0 #30b1b6, 140px 0 #36b4b0, 154px 0 #37b4ac, 168px 0 #39b6a6,
    182px 0 #3db7a0, 196px 0 #40b89c, 210px 0 #41b89a, 224px 0 #42b89a,
    238px 0 #43b89a, 252px 0 #44b89a, 266px 0 #45b89a, 280px 0 #46b89a,
    294px 0 #47b89a, 308px 0 #48b89a;
}
</style>

 3. The third chart "Stripe Animation-3-", this chart is an upgraded version of the first chart, which introduces animation properties on the basis of the background, and realizes a progress bar with animation through uninterrupted position offset ,code show as below:

<!-- stripe.vue -->
<template>
  <div class="container">
    <div class="type-box" v-for="(item, index) in dataList" :key="index">
      <div class="type-name">{
   
   { item.name }}</div>
      <div class="type-progress">
        <div class="type-percent" :style="{ width: item.rate }"></div>
      </div>
      <div class="type-num">{
   
   { item.rate }}</div>
    </div>
  </div>
</template>
<script>
export default {
  name: "Stripe",
  data() {
    return {
      dataList: [
        { name: "濮阳县", value: 92.45, rate: "92.45%" },
        { name: "清丰县", value: 64.55, rate: "64.55%" },
        { name: "范县", value: 50.25, rate: "50.25%" },
        { name: "台前县", value: 25.38, rate: "25.38%" },
        { name: "南乐县", value: 10.04, rate: "10.04%" },
      ],
    };
  },
};
</script>
<style scoped>
.container {
  display: flex;
  width: 100%;
  height: 100%;
  justify-content: center;
  flex-wrap: wrap;
  align-content: space-between;
}
.type-box {
  display: flex;
  align-items: center;
  width: 100%;
  height: 40px;
}

.type-name {
  width: 60px;
  font-size: 16px;
  color: #fff;
}

.type-num {
  width: 70px;
  font-size: 16px;
  color: #fff;
  text-align: right;
}

.type-progress {
  flex: 1;
  height: 24px;
  border: 1px solid #1a76e3;
  border-radius: 6px;
}

.type-percent {
  height: 100%;
  border-radius: 4px;
  /* 核心代码 */
  background: linear-gradient(
      to right bottom,
      #41b89a 25%,
      #252d6e 25%,
      #252d6e 50%,
      #41b89a 50%,
      #41b89a 75%,
      #252d6e 75%
    )
    0 0/24px 24px;
  animation: animate 1s linear infinite;
}

@keyframes animate {
  0% {
    background-position-x: 0;
  }

  100% {
    background-position-x: 24px;
  }
}
</style>

4. The fourth chart "striped progress bar-4-", this chart is realized by stacking three layers, which are light-colored background layer, colored progress layer and the uppermost dividing line layer (the color of the dividing line and the background Layer consistency is the key to implementation), the specific code is as follows:

<!-- gapline.vue -->
<template>
  <div class="container" :style="{ '--skew': skew + 'deg' }">
    <div class="type-box" v-for="(item, index) in dataList" :key="index">
      <div class="type-name">{
   
   { item.name }}</div>
      <div class="type-progress">
        <div class="progress" :style="{ width: item.rate }"></div>
      </div>
      <div class="type-num">{
   
   { item.rate }}</div>
    </div>
  </div>
</template>
<script>
export default {
  name: "GapLine",
  props: {
    skew: {
      type: Number,
      default: 0,
    },
  },
  data() {
    return {
      dataList: [
        { name: "濮阳县", value: 92.45, rate: "92.45%" },
        { name: "清丰县", value: 64.55, rate: "64.55%" },
        { name: "范县", value: 50.25, rate: "50.25%" },
        { name: "台前县", value: 25.38, rate: "25.38%" },
        { name: "南乐县", value: 10.04, rate: "10.04%" },
      ],
    };
  },
};
</script>
<style scoped>
.container {
  display: flex;
  width: 100%;
  height: 100%;
  justify-content: center;
  flex-wrap: wrap;
  align-content: space-between;
}
.type-box {
  display: flex;
  align-items: center;
  width: 100%;
  height: 40px;
}

.type-name {
  width: 60px;
  font-size: 16px;
  color: #fff;
}

.type-progress {
  position: relative;
  flex: 1;
  height: 20px;
  transform: skew(var(--skew));
}
.type-progress::before,
.type-progress::after {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
}
/* 浅色背景层 */
.type-progress::before {
  z-index: -1;
  background-color: #252d6e;
}
/* 分割线层 */
.type-progress::after {
  z-index: 1;
  background: linear-gradient(to right, transparent 80%, #150a46 80%) 0 0/18px
    100% repeat-x;
}
/* 进度层 */
.progress {
  width: 100%;
  height: 100%;
  background: linear-gradient(to right, #0db7f2, #00de95);
}
.type-num {
  width: 70px;
  font-size: 16px;
  color: #fff;
  text-align: right;
}
</style>

 5. The fifth chart "Rounded Corner Progress Bar-5-", this kind of chart has a wide range of application scenarios and is frequently used in large-screen visualization. Compared with the above 4 chart implementation methods, it is also simpler. This chart is It consists of two stacked layers, a light-colored background layer and a colored progress layer. Of course, it can be combined with the gradient feature to achieve a more personalized progress bar display effect. The code is as follows:

<!-- gradientbar.vue -->
<template>
  <div class="container" :style="{ '--background': bgColor }">
    <div class="type-box" v-for="(item, index) in dataList" :key="index">
      <div class="type-name">{
   
   { item.name }}</div>
      <div class="type-progress">
        <div class="progress" :style="{ width: item.rate }"></div>
      </div>
      <div class="type-num">{
   
   { item.rate }}</div>
    </div>
  </div>
</template>
<script>
export default {
  name: "GradientBar",
  props: {
    bgColor: {
      type: String,
      default: 'linear-gradient(to right, #0db7f2, #00de95)',
    },
  },
  data() {
    return {
      dataList: [
        { name: "濮阳县", value: 92.45, rate: "92.45%" },
        { name: "清丰县", value: 64.55, rate: "64.55%" },
        { name: "范县", value: 50.25, rate: "50.25%" },
        { name: "台前县", value: 25.38, rate: "25.38%" },
        { name: "南乐县", value: 10.04, rate: "10.04%" },
      ],
    };
  },
};
</script>
<style scoped>
.container {
  display: flex;
  width: 100%;
  height: 100%;
  justify-content: center;
  flex-wrap: wrap;
  align-content: space-between;
}
.type-box {
  display: flex;
  align-items: center;
  width: 100%;
  height: 40px;
}

.type-name {
  width: 60px;
  font-size: 16px;
  color: #fff;
}

.type-progress {
  position: relative;
  flex: 1;
  height: 16px;
  border-radius: 10px;
  background-color: #252d6e;
}
.progress {
  width: 100%;
  height: 100%;
  border-radius: 10px;
  /* 核心代码 */
  background: var(--background);
}
.type-num {
  width: 70px;
  font-size: 16px;
  color: #fff;
  text-align: right;
}
</style>

 6. The sixth chart "Pseudo 3D columnar progress bar -6-", the implementation of this kind of chart is not complicated, in fact, it can be regarded as composed of three parts, the cylinder, the head, and the tail; the difficulty of realization lies in how to add Marker graphics at the head and tail. Here, the clip-path attribute in the CSS property is used to clip the specified area to realize various marker graphics. The code is as follows:

<!-- linecircle.vue -->
<template>
  <div class="bar-box">
    <div
      v-for="item in dataList"
      :key="item.id"
      :style="{ height: item.rate }"
      class="bar-item"
    >
      <div class="bar-line"></div>
    </div>
  </div>
</template>
<script>
export default {
  name: "LineCircle",
  data() {
    return {
      dataList: [
        { id: 1, rate: "25%" },
        { id: 2, rate: "100%" },
        { id: 3, rate: "50%" },
        { id: 4, rate: "40%" },
        { id: 5, rate: "75%" },
        { id: 6, rate: "90%" },
        { id: 7, rate: "10%" },
        { id: 8, rate: "66%" },
      ],
    };
  },
};
</script>
<style scoped>
.bar-box {
  display: flex;
  align-items: flex-end;
  justify-content: space-around;
  padding: 20px;
  box-sizing: border-box;
  width: 100%;
  height: 100%;
}
.bar-item {
  position: relative;
  width: 30px;
  height: 0;
  box-sizing: border-box;
  transition: height 1s ease;
}
/* 核心代码 */
.bar-box .bar-item:nth-child(4n + 1) {
  background: linear-gradient(
    to top,
    rgba(54, 205, 232, 1),
    rgba(54, 205, 232, 0.6)
  );
  --marker-bg: rgb(54, 205, 232);
  --clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0 50%);
  --line-opacity: 1;
}
.bar-box .bar-item:nth-child(4n + 2) {
  background: linear-gradient(
    to top,
    rgba(54, 100, 255, 1),
    rgba(54, 100, 255, 0.6)
  );
  --marker-bg: rgb(54, 100, 255);
  --clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0 50%);
  --line-opacity: 1;
}
.bar-box .bar-item:nth-child(4n + 3) {
  background: linear-gradient(
    to top,
    rgba(249, 152, 45, 1),
    rgba(249, 152, 45, 0.6)
  );
  --marker-bg: rgb(249, 152, 45);
  --clip-path: none;
  --line-opacity: 0;
}
.bar-box .bar-item:nth-child(4n) {
  background: linear-gradient(
    to top,
    rgba(0, 220, 153, 1),
    rgba(0, 220, 153, 0.6)
  );
  --marker-bg: rgb(0, 220, 153);
  --clip-path: none;
  --line-opacity: 0;
}
/** 柱子中间的线条,模拟棱角 */
.bar-item .bar-line {
  position: absolute;
  left: 50%;
  top: 0;
  width: 1px;
  height: 100%;
  background: var(--marker-bg);
  opacity: var(--line-opacity);
}
/* 柱子的上下头部添加两个平行四边形 */
.bar-item::before,
.bar-item::after {
  content: "";
  position: absolute;
  left: 0;
  width: 100%;
  /* 高度始终是宽度的一半 */
  padding-top: 50%;
  border-radius: 50%;
  clip-path: var(--clip-path);
  z-index: 10;
  background-color: var(--marker-bg);
}
.bar-item::before {
  top: 0;
  transform: translateY(-50%);
}
.bar-item::after {
  bottom: 0;
  transform: translateY(50%);
}
</style>

Of course, the implementation schemes of linear progress bars are not limited to the above ones. Solid CSS basic knowledge combined with personal whimsy can realize various progress bars. If you have other better implementation schemes, please share them Let's make progress together and grow together.

Guess you like

Origin blog.csdn.net/qq_40289557/article/details/123241697