SVG draws a circular progress bar

In our large-screen visualization projects, various charts are often used. Compared with the traditional table display and boring text description, the graph display makes the user look more intuitive and the data display is more clear at a glance. Based on the svg drawing technology combined with the front-end technology stack vue, this article takes the circular progress bar commonly used in work as an example to briefly explain some ideas for drawing the progress bar. Refer to another article.

Several implementations of linear progress bars https://blog.csdn.net/qq_40289557/article/details/123241697

Idea explanation: There are various selection techniques for drawing a circle, for example, you can use canvas to draw, use div to simulate, use echarts plug-in, etc. Here we take svg technology as an example. Speaking of drawing circles, we can use the circle tag in svg, of course, we can also use the path tag, one ring is used as the background, and the other ring is used as the progress ring. There is a question here, since they are all circles, how to draw the corresponding radians according to the actual values? Here you need to use an advanced css attribute, stroke-dashoffset- for specific usage, please refer to the relevant api documentation. In most business scenarios, the ring-shaped progress bar is always oddly shaped. For example, some ring-shaped progress bars seem to have scales, one by one, which requires the use of another advanced css attribute, stroke-dasharray. Using stroke-dashoffset and stroke-dasharray, combined with knowledge points such as gradient and rotation in svg, we can draw various progress bars, as shown below:

Based on the above ideas, first we encapsulate a progress bar component, the code is as follows

<template>
  <div class="progress">
    <svg
      :style="{ transform: clockWise ? 'rotateY(0deg)' : 'rotateY(180deg)' }"
      xmlns="http://www.w3.org/2000/svg"
      width="100%"
      height="100%"
      viewBox="0 0 400 400"
    >
      <!-- 定义渐变色 -->
      <defs v-if="showGradient">
        <linearGradient
          :id="gradient.id"
          :x1="gradient.x1"
          :y1="gradient.y1"
          :x2="gradient.x2"
          :y2="gradient.y2"
        >
          <stop
            v-for="(item, index) in gradient.colorStops"
            :key="index"
            :offset="item.offset"
            :stop-color="item.color"
          />
        </linearGradient>
      </defs>
      <!-- 底部背景圆环 -->
      <circle
        cx="200"
        cy="200"
        :r="radius"
        :stroke="bgRingColor"
        :stroke-width="strokeWidth"
        fill="none"
      />
      <!-- 进度条圆环 -->
      <circle
        class="progress-bar"
        cx="200"
        cy="200"
        :r="radius"
        :stroke="showGradient ? `url(#${gradient.id})` : ringColor"
        :stroke-width="strokeWidth"
        :stroke-linecap="strokeLinecap"
        fill="none"
        transform="rotate(-90, 200, 200)"
        :stroke-dasharray="strokeDasharray"
        :stroke-dashoffset="strokeDashoffset"
      />
      <!-- 环形分割细线 -->
      <circle
        cx="200"
        cy="200"
        :r="radius"
        stroke="#071727"
        fill="transparent"
        :stroke-width="strokeWidth + 2"
        :stroke-dasharray="strokeDasharray1"
        transform="rotate(-90, 200, 200)"
      ></circle>
    </svg>
    <!-- 中间描述文字 -->
    <div class="progress-desc">
      <slot></slot>
    </div>
  </div>
</template>
<script>
export default {
  name: "Progress",
  props: {
    rate: {
      type: Number,
      default: 0,
    },
    bgRingColor: {
      type: String,
      default: "#25b489",
    },
    ringColor: {
      type: String,
      default: "#67C23A",
    },
    strokeLinecap: {
      type: String,
      default: "round",
    },
    strokeWidth: {
      type: Number,
      default: 20,
    },
    part: {
      type: Number,
      default: 20,
    },
    partGap: {
      type: Number,
      default: 12,
    },
    showGradient: {
      type: Boolean,
      default: false,
    },
    clockWise: {
      type: Boolean,
      default: true,
    },
    gradient: {
      type: Object,
      default: () => {
        return {
          id: "svg-linear-gradient",
          x1: "0%",
          y1: "0%",
          x2: "0%",
          y2: "100%",
          colorStops: [
            {
              offset: "0%",
              color: "yellow",
            },
            {
              offset: "100%",
              color: "orange",
            },
          ],
        };
      },
    },
  },
  computed: {
    radius() {
      return 200 - this.strokeWidth / 2;
    },
    strokeDasharray() {
      let value = 0;
      /* 
        当stroke-linecap的值为round时,结尾处会向外延伸描边宽度的一半
        首尾两个端点,所以就是描边的宽度
      */
      if (this.strokeLinecap === "round") {
        value = 2 * Math.PI * this.radius + this.strokeWidth;
      } else {
        value = 2 * Math.PI * this.radius;
      }
      return value;
    },
    strokeDashoffset() {
      let val = 0;
      let rate = this.rate;
      if (rate > 1) {
        rate = 1;
      } else if (rate < 0) {
        rate = 0;
      }
      if (this.strokeLinecap === "round") {
        val = 2 * Math.PI * this.radius * (1 - rate) + this.strokeWidth;
      } else {
        val = 2 * Math.PI * this.radius * (1 - rate);
      }
      return val;
    },
    strokeDasharray1() {
      var partLength = (2 * Math.PI * this.radius) / this.part;
      return `${this.partGap} ${partLength - this.partGap}`;
    },
  },
};
</script>
<style scoped>
.progress {
  position: relative;
  width: 100%;
  height: 100%;
}
.progress-desc {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  font-family: "Microsoft YaHei";
  font-size: 20px;
  text-align: center;
}
.progress-bar {
  transition: stroke-dashoffset 2s ease;
}
</style>

At this point, we have encapsulated the basic properties of the progress bar and some required parameters, and we can use this progress bar component in the corresponding component. By configuring different styles, we can complete various small cases up

<template>
  <div class="progress">
    <div class="svg-module">
      <div class="module-title">条纹进度条一</div>
      <div class="module-content">
        <svg-line
          :rate="0.3456"
          bg-ring-color="#25b489"
          ring-color="#ec6841"
          :stroke-width="20"
          stroke-linecap="butt"
          :part="20"
          :part-gap="12"
        >
        </svg-line>
      </div>
    </div>
    <div class="svg-module">
      <div class="module-title">条纹进度条二</div>
      <div class="module-content">
        <svg-line
          :rate="0.8825"
          bg-ring-color="#446224"
          ring-color="#a3fe49"
          :stroke-width="20"
          stroke-linecap="butt"
          :part="20"
          :part-gap="12"
        >
          <span>
            <span>{
   
   { `${(0.8825 * 100).toFixed(2)}%` }}</span>
          </span>
        </svg-line>
      </div>
    </div>

    <div class="svg-module">
      <div class="module-title">条纹进度条三</div>
      <div class="module-content">
        <svg-line
          :rate="0.8888"
          bg-ring-color="#13387e"
          ring-color="#1cbd9c"
          :stroke-width="30"
          stroke-linecap="butt"
          :part="40"
          :part-gap="8"
          :show-gradient="true"
          :gradient="myGradient"
        >
          <span>
            <span>渐变进度条</span><br />
            <span>{
   
   { `${(0.8888 * 100).toFixed(2)}%` }}</span>
          </span>
        </svg-line>
      </div>
    </div>

    <div class="svg-module">
      <div class="module-title">条纹进度条四</div>
      <div class="module-content">
        <svg-line
          :rate="0.5035"
          bg-ring-color="#13387e"
          ring-color="#00bbff"
          stroke-linecap="butt"
          :stroke-width="20"
          :part="50"
          :part-gap="20"
        >
          <span>
            <span>{
   
   { `${(0.5035 * 100).toFixed(2)}%` }}</span>
            <br />
            <span>完成率</span>
          </span>
        </svg-line>
      </div>
    </div>

    <div class="svg-module">
      <div class="module-title">条纹进度条五</div>
      <div class="module-content">
        <svg-line
          :rate="0.7667"
          bg-ring-color="#13387e"
          ring-color="orange"
          :stroke-width="26"
          stroke-linecap="butt"
          :part="1"
          :part-gap="0"
          :clock-wise="false"
        >
          <span>
            <span>普通进度条</span><br />
            <span>{
   
   { `${(0.7667 * 100).toFixed(2)}%` }}</span>
          </span>
        </svg-line>
      </div>
    </div>

    <div class="svg-module">
      <div class="module-title">条纹进度条六</div>
      <div class="module-content">
        <svg-line
          :rate="0.7685"
          bg-ring-color="#13387e"
          ring-color="#00bbff"
          :stroke-width="20"
          :part="1"
          :part-gap="0"
        >
          <span>
            <span>带弧度进度条</span><br />
            <span>{
   
   { `${(0.7685 * 100).toFixed(2)}%` }}</span>
          </span>
        </svg-line>
      </div>
    </div>

    <div class="svg-module">
      <div class="module-title">条纹进度条七</div>
      <div class="module-content">
        <svg-line
          :rate="0.3333"
          bg-ring-color="#13387e"
          ring-color="#00bbff"
          :stroke-width="20"
          :part="1"
          :part-gap="0"
          :show-gradient="true"
          :clock-wise="false"
        >
          <span>
            <span>渐变进度条</span><br />
            <span>{
   
   { `${(0.3333 * 100).toFixed(2)}%` }}</span>
          </span>
        </svg-line>
      </div>
    </div>

    <div class="svg-module">
      <div class="module-title">条纹进度条八</div>
      <div class="module-content">
        <svg-line
          :rate="0.5"
          bg-ring-color="#ee6666"
          ring-color="#fac858"
          :stroke-width="200"
          stroke-linecap="butt"
          :part="1"
          :part-gap="0"
        >
        </svg-line>
      </div>
    </div>
  </div>
</template>
<script>
import SvgLine from "./svgline.vue";
export default {
  name: "SVGLineIndex",
  components: {
    SvgLine,
  },
  data() {
    return {
      myGradient: {
        // 同一个页面中id务必唯一
        id: "svg-linear-gradient001",
        x1: "100%",
        y1: "100%",
        x2: "0%",
        y2: "0%",
        colorStops: [
          {
            offset: "0%",
            color: "#0ae787",
          },
          {
            offset: "100%",
            color: "#fe653c",
          },
        ],
      },
    };
  },
};
</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: 23%;
  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;
}
</style>

Guess you like

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