SVG绘制圆环进度条

2023-05-16

在我们的大屏可视化项目中,经常会用到各种各样的图表。与传统的表格展示、枯燥的文字阐述相比,图表展示则使用户看起来更加直观、数据的展示则更加一目了然。本文基于svg绘图技术结合前端技术栈vue,以工作中常用的环形进度条为例,简单阐述一下绘制进度条的一些思路,希望能和大家共勉,关于“线形进度条的几种实现方式”可以参考另一篇文章。

线形进度条的几种实现方式https://blog.csdn.net/qq_40289557/article/details/123241697

思路讲解:绘制圆环,选型技术有多样,比如可以使用canvas绘制、使用div模拟,使用echarts插件等等,这里以svg技术为例。说起绘制圆环,我们可以使用svg中的circle标签,当然也可以使用path标签,一个圆环作为背景,另一个圆环作为进度环。这里就有一个疑问,既然都是圆,怎么根据实际的数值绘制对应的弧度呢?这里就需要用到一个高级的css属性,stroke-dashoffset-具体用法可以查看相关api文档。在大多数的业务场景中,环形进度条总是奇形怪状,例如有的环形进度条像是带有刻度的,一段一段的,这就需要用到另一个高级css属性了,stroke-dasharray。使用stroke-dashoffset和stroke-dasharray,结合svg中的渐变和旋转等知识点,我们可以绘制出各种各样的进度条,如下图:

基于以上思路,首先我们来封装一个进度条组件,代码如下

<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>

到这里,我们封装了进度条的基本属性及一些必传的参数,我们就可以在在相应的组件中使用这个进度条组件了,通过配置不同的样式,就可以完成各种各样的小案例了

<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>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SVG绘制圆环进度条 的相关文章

随机推荐

  • seL4的编译和运行(OdroidXU3)

    seL4支持平台 seL4所在的git如下 xff1a https github com seL4 seL4 git 不过我们编译和运行seL4一般不只是用这个git xff0c 而是借助seL4test xff08 seL4的测试框架 x
  • 目标跟踪之Pysot系列代码训练(SiamRPN\SiamRPN++)

    代码地址 xff1a https github com STVIR pysot 环境配置参考博客 xff1a Siam系列跟踪算法工具包PySOT配置 一 数据集准备 Pysot系列跟踪器训练的时候 xff0c 首先将数据集进行裁剪 xff
  • 基于Paddle实现实例分割

    百度的Paddle这几年发展十分迅速 xff0c 而且文档十分齐全 xff0c 涉及到机器视觉的多个应用领域 xff0c 感觉还是非常牛的 xff0c 各种backbone xff0c 损失函数 数据增强手段以及NMS等 xff0c 整体感
  • 机器学习笔记: 时间序列 分解 STL

    1 前言 STL Seasonal and Trend decomposition using Loess 是以LOSS 作为平滑方式的时间序列分解 LOSS可以参考机器学习笔记 xff1a 局部加权回归 LOESS UQI LIUWJ的博
  • C++11 auto遍历

    C 43 43 11这次的更新带来了令很多C 43 43 程序员期待已久的for range循环 xff0c 每次看到javascript xff0c lua里的for range xff0c 心想要是C 43 43 能有多好 xff0c
  • C++ 文件的读写(fin && fout)

    如何让键盘输入字符保存在 txt文件中 如何让我们自己在键盘上输入的字符不仅仅在屏幕上显示 xff0c 而且还能保存在特定路径的文件中 xff0c 这让简单枯燥的控制台命令程序变得略有趣 首先 xff0c 先看看cin和cout对象 xff
  • 基本矩阵、本质矩阵和单应矩阵

    两幅视图存在两个关系 xff1a 第一种 xff0c 通过对极几何一幅图像上的点可以确定另外一幅图像上的一条直线 xff1b 另外一种 xff0c 通过上一种映射 xff0c 一幅图像上的点可以确定另外一幅图像上的一个点 xff0c 这个点
  • 矩阵零空间

    矩阵A的零空间就Ax 61 0的解的集合 零空间的求法 xff1a 对矩阵A进行消元求得主变量和自由变量 xff1b 给自由变量赋值得到特解 xff1b 对特解进行线性组合得到零空间 假设矩阵如下 xff1a 对矩阵A进行高斯消元得到上三角
  • VIO学习总结

    VIO xff08 visual inertial odometry xff09 即视觉惯性里程计 xff0c 有时也叫视觉惯性系统 xff08 VINS xff0c visual inertial system xff09 xff0c 是
  • 单应性(Homography)变换

    我们已经得到了像素坐标系和世界坐标系下的坐标映射关系 xff1a 其中 xff0c u v表示像素坐标系中的坐标 xff0c s表示尺度因子 xff0c fx fy u0 v0 xff08 由于制造误差产生的两个坐标轴偏斜参数 xff0c
  • senmantic slam mapping

    basicStructure hpp common h 定义一些常用的结构体 以及各种可能用到的头文件 xff0c 放在一起方便include 相机内参模型 增加了畸变参数 xff0c common headers h各种可能用到的头文件
  • Ubuntu 20.04 VNC 安装与设置

    原链接 VNC是一个远程桌面协议 按照本文的说明进行操作可以实现用VNC对Ubuntu 20 04进行远程控制 一般的VNC安装方式在主机没有插显示器的时候是无法使用的 下面的操作可以在主机有显示器和没有显示器时都能够正常工作 首先安装x1
  • opencv中类型转换问题

    记录一下最近困惑我的问题 方便以后查阅 在学习立体匹配算法中BM算法时 xff0c 出现在了关于类型转换的问题 xff1a disp convertTo disp8u CV 8U 255 numberOfDisparities 16 不知道
  • 最大似然估计MLE与贝叶斯估计

    最大似然估计 Maximum Likehood Estimation MLE 最大似然估计的核心思想是 xff1a 找到参数 的一个估计值 xff0c 使得当前样本出现的可能性最大 用当年博主老板的一句话来说就是 xff1a 谁大像谁 xf
  • 大疆Livox_mid 40雷达初体验

    为了解决无人车上镭神雷达FOV小而导致的车前3m内无法看到锥形桶问题 东家给公司邮寄了一台大疆的mid40雷达 不得不说 颜值真的高 光看颜值 就甩镭神几条街 昨天重新配置镭神的激光雷达 官方给的配置软件 真的是 用的我心碎啊 算了 不提了
  • 地铁供电系统的构成

    地铁供电系统一般划分为以下几部分 xff1a 外部电源 xff1b 主变电所 xff1b 牵引供电系统 xff1b 动力照明系统和杂散电流腐蚀防护系统 xff1b 电力监控系统 外部电源地铁供电系统的外部电源就是地铁供电系统主变电所供电的外
  • C++ Vector常用函数

    C 43 43 Vector常用函数 begin 函数 原型 xff1a iterator begin const iterator begin 功能 xff1a 返回一个当前vector容器中起始元素的迭代器 end 函数 原型 xff1
  • STM32使用ADC获取内部温度传感器数据输出(直接读取/DMA两种方式实现)

    STM32使用ADC获取内部温度传感器数据输出 xff08 直接读取 DMA两种方式实现 xff09 前言一 内部温度传感器的使用 xff1f 二 代码操作讲解1 直接读取2 DMA处理 总结 前言 STM32F1系列 xff08 本代码基
  • fp32/fp64精度,4/8字节16进制转float/double十进制

    1 IEEE 754 32位单精度浮点数 xff08 4字节 xff09 1 1 32位单精度浮点数 其中 xff0c 32位16进制数包括1位符号位 SIGN xff0c 8位指数位 EXPONENT 和 23位尾数位 MANTISSA
  • SVG绘制圆环进度条

    在我们的大屏可视化项目中 xff0c 经常会用到各种各样的图表 与传统的表格展示 枯燥的文字阐述相比 xff0c 图表展示则使用户看起来更加直观 数据的展示则更加一目了然 本文基于svg绘图技术结合前端技术栈vue xff0c 以工作中常用