SVG怎么画渐变甜甜圈(进度环)

SVG画个渐变进度甜甜圈

需求场景

经常画图的同学应该都知道,ECharts不仅会受版本影响而且各种类型的图表配置还些许不同,更何况版本不一时配置也会存在天差地别的可能,一想到这~~~就非常的让人秃然 ,阿布!秃头 😒😒😒

于是不想使用ECharts图表,但又要实现评分、环比等进度环的图形样式,而且还需要增加过渡动效。起始点、、执行方向根据需求自行定义即可,废话少说,直接上图👇

图例

代码分析

一、svg元素
javascript 复制代码
<svg width="21vw" height="21vw" viewBox="0 0 280 280" preserveAspectRatio="xMidYMid meet" class="progress-ring-svg">

1. width / height: 设置SVG的宽度和高度,这里使用了相对单位vw,使其在不同设备上具有自适应的特性。
2. viewBox: 定义了可视区域,其格式为viewBox="minX minY width height"。在这里,它指定了一个280x280的坐标系统,SVG内容将根据这个区域进行缩放。
3. preserveAspectRatio: 设置图形在缩放时如何保持纵横比。xMidYMid meet表示图形在水平和垂直方向上居中,并保持原始比例。

二、定义渐变
javascript 复制代码
<defs>  
  <linearGradient id="progressGradient" x1="0%" y1="0%" x2="100%" y2="100%" style="transform: rotate(-35deg)">  
    <stop offset="20.74%" style="stop-color: rgb(247, 121, 111); stop-opacity: 1"></stop>  
    <stop offset="54.15%" style="stop-color: rgb(201, 98, 230); stop-opacity: 1"></stop>  
    <stop offset="82.63%" style="stop-color: rgb(89, 89, 244); stop-opacity: 1"></stop>  
  </linearGradient>  
</defs>

1. defs: 用于定义可复用的元素,如渐变、图形等。
2. linearGradient: 定义线性渐变,id是渐变的标识符,后面可以通过url(#progressGradient)引用它。
3. x1, y1, x2, y2: 定义渐变的起始和结束坐标(百分比)。
4. transform: 旋转渐变的方向。
5. stop: 定义渐变的颜色和透明度。
6. offset: 指定渐变中颜色的起始位置(相对于渐变的长度)。
7. stop-color: 设置颜色。
8. stop-opacity: 设置透明度。

三、圆形元素
javascript 复制代码
<circle stroke="transparent" stroke-width="39" fill="transparent" r="119" cx="139" cy="141" class="progress-ring-circle"></circle>

1. stroke: 设置圆的边框颜色,这里是透明的。
2. stroke-width: 设置边框的宽度。
3. fill: 设置填充颜色,这里是透明的。
4. r: 圆的半径。
5. cx, cy: 圆心的坐标。
6. class: 用于样式和脚本的选择器。

四、进度圆
javascript 复制代码
<circle stroke="url(#progressGradient)" stroke-width="39" fill="transparent" r="119" cx="140" cy="141.5" class="progress-ring-circle progress" style="stroke-dasharray: 904.699; stroke-dashoffset: 1436.004; stroke-linecap: round; transition: stroke-dashoffset 1s ease;"></circle>

1. stroke: 设置边框颜色为之前定义的渐变。
2. stroke-width: 边框的宽度,与背景圆相同。
3. r, cx, cy: 同样设置圆的半径和圆心坐标。
4. stroke-dasharray: 定义了虚线的模式,这里是一个完整的圆周长(周长根据设定的radius值进行动态计算>>2 *

Math.PI * radius),用于实现进度显示。
5. stroke-dashoffset: 定义了虚线的偏移量,控制可见部分的长度。该属性的设置和动画效果是关键要素,这些决定了动画的显示效果及其方向。如果其值较大(如747.699),将使得圆环的绘制方向呈现逆时针效果,模拟一种"从后往前"的绘制过程。
6. stroke-linecap: 设置圆角样式,这里为round,使得圆的端点为圆形。
7. transition: 设置动画效果,当stroke-dashoffset变化时,动画持续时间为1秒,使用ease过渡效果。
8. offset: 这是一个初始值设置,表示进度圆当前的stroke-dashoffset。这个值是圆环周长的一部分,用于控制可见部分的长度。
9. 逆时针动画: 如果stroke-dashoffset的初始值设置为零,则在动画过程中,圆环的进度将从圆环的起始位置(通常是顶部)开始向前(顺时针)绘制。但是,通过将初始offset设置为一个正值(如747.699),可以使进度在动画开始时向反方向(逆时针)延伸,从而避免在默认状态下看到未绘制的部分。

示例代码

javascript 复制代码
    <!-- 
  简要总结viewBox的作用:
    定义可视区域:viewBox属性的格式为viewBox="minX minY width height",它确定了SVG内容的可视区域和比例。
    保证缩放:当你改变SVG的宽高时,viewBox确保图形在缩放时不会失去比例或被裁剪。
    适应不同设备:使用preserveAspectRatio属性配合viewBox,能够让SVG在各种设备上自适应显示。
-->
<!-- 
  <svg :width="h_w_" :height="h_w_" class="progress-ring-svg" style="transform: scaleX(-1) rotate(275deg);"
    viewBox="0 0 280 280" preserveAspectRatio="xMidYMid meet">
    <defs>
      <linearGradient id="progressGradient" x1="0%" y1="0%" x2="100%" y2="100%"
        style="transform: rotate(-35deg);">
        <stop offset="20.74%" style="stop-color: #F7796F; stop-opacity: 1;"></stop>
        <stop offset="54.15%" style="stop-color: #C962E6; stop-opacity: 1;"></stop>
        <stop offset="82.63%" style="stop-color: #5959F4; stop-opacity: 1;"></stop>
      </linearGradient>
    </defs>
    <circle stroke="transparent" :stroke-width="strokeWidth" fill="transparent" :r="radius" :cx="cx" :cy="cy"
      class="progress-ring-circle"></circle>
    <circle :stroke="'url(#progressGradient)'" :stroke-width="strokeWidth" fill="transparent" :r="radius"
      :cx="cx" :cy="cy" class="progress-ring-circle progress"
      :style="{ strokeDasharray: circumference, strokeDashoffset: offset, strokeLinecap: 'round' }">
    </circle>
  </svg>
-->

<div class="I-KUN">
  <div class="progress-ring">
    <svg
      width="21vw"
      height="21vw"
      viewBox="0 0 280 280"
      preserveAspectRatio="xMidYMid meet"
      class="progress-ring-svg"
    >
      <defs>
        <linearGradient
          id="progressGradient"
          x1="0%"
          y1="0%"
          x2="100%"
          y2="100%"
          style="transform: rotate(-35deg)"
        >
          <stop
            offset="20.74%"
            style="stop-color: rgb(247, 121, 111); stop-opacity: 1"
          ></stop>
          <stop
            offset="54.15%"
            style="stop-color: rgb(201, 98, 230); stop-opacity: 1"
          ></stop>
          <stop
            offset="82.63%"
            style="stop-color: rgb(89, 89, 244); stop-opacity: 1"
          ></stop>
        </linearGradient>
      </defs>
      <circle
        stroke="transparent"
        stroke-width="39"
        fill="transparent"
        r="119"
        cx="139"
        cy="141"
        class="progress-ring-circle"
      ></circle>
      <circle
        stroke="url(#progressGradient)"
        stroke-width="39"
        fill="transparent"
        r="119"
        cx="140"
        cy="141.5"
        class="progress-ring-circle progress"
        style="
          stroke-dasharray: 904.699;
          stroke-dashoffset: 1436.004;
          stroke-linecap: round;
          transition: stroke-dashoffset 1s ease;
        "
      ></circle>
    </svg>
  </div>
  <div class="contet">
    <div class="title">2.5<span class="K">KunBi</span></div>
    <div class="desc opacity_half">练习时长两年半的前端</div>
  </div>
  <div class="tooltip">
    <div class="row_1">
      <span class="img_name"><span class="name">唱跳Rap篮球🏀</span></span
      ><span class="num">2.5<span class="P">Kun</span> </span>
    </div>
    <div class="row_2">
      <span class="img_name"> <span class="name">你小子露鸡爪🤭</span></span
      ><span class="num">2.5<span class="P">Kun</span> </span>
    </div>
  </div>
</div>

data() { return { percentage: 0, r:119, cx: 139, cy: 140, offset: 747.699, //
默认赋周长值,否则 出现逆时针动画效果 percent: 0, foldableScreen:
foldableScreen(), h_w_: foldableScreen() ? '21vw' : '71.822667vw' } },

<style lang="scss">
  .I-KUN {
    width: 390px;
    height: 390px;
    display: flex;
    justify-content: center;
    align-items: center;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    background: linear-gradient(
      98.51deg,
      rgb(175 164 230 / 34%) 1.35%,
      rgb(152 50 170 / 6%) 103.86%
    );
  }
  .progress-ring {
    position: absolute;
  }
  .progress-ring-circle {
    transition: stroke-dashoffset 1s ease;
  }
  .contet {
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    position: absolute;
    width: 15vw;
    height: 15vw;
    border-radius: 50%;
    background: #ffffff;
    box-shadow: 1px 1px 38px #baadd8;
  }
  .title {
    font-family: "PingFang SC";
    font-size: 36.67px;
    font-weight: 600;
    line-height: 36.67px;
  }
  .K {
    margin-left: 5px;
    font-size: 22px;
    line-height: 22px;
  }
  .desc {
    margin-top: 9.6px;
    font-family: "Alibaba PuHuiTi 2.0";
    font-size: 13.67px;
    font-weight: 400;
    line-height: 13.93px;
    letter-spacing: 0.01em;
    color: #909090;
  }
  .tooltip {
    padding: 18px 18px 18px 20px;
    backdrop-filter: blur(18px);
    box-shadow: 1.33px 1.33px 0px 0px #ffffff99 inset;
    background: linear-gradient(
      98.51deg,
      rgba(248, 247, 253, 0.48) 1.35%,
      rgba(248, 247, 253, 0.8) 103.86%
    );
    min-width: 260.33px;
    height: auto;
    border-radius: 20px;
    position: absolute;
    bottom: 1vw;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
  }
  .row_1,
  .row_2 {
    width: 100%;
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-family: "Alibaba PuHuiTi 2.0";
    font-size: 20px;
    font-weight: 400;
    line-height: 20.14px;
    margin-bottom: 13.33px;
  }
  .row_2 {
    margin-bottom: 0;
  }
  .name {
    max-width: 30vw;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .num {
    font-size: 18px;
    font-weight: 700;
    line-height: 20.14px;
  }
  .P {
    font-size: 12px;
    font-weight: 700;
    line-height: 12px;
    margin-left: 3px;
    color: #a0a0a0;
  }
</style>
相关推荐
a栋栋栋3 小时前
apifox
java·前端·javascript
请叫我飞哥@4 小时前
HTML 标签页(Tabs)详细讲解
前端·html
Anlici4 小时前
React18与Vue3组件通信对比学习(详细!建议收藏!!🚀🚀)
前端·vue.js·react.js
m0_748251524 小时前
PDF在线预览实现:如何使用vue-pdf-embed实现前端PDF在线阅读
前端·vue.js·pdf
中生代技术5 小时前
3.从制定标准到持续监控:7个关键阶段提升App用户体验
大数据·运维·服务器·前端·ux
m0_748239335 小时前
从零开始:如何在.NET Core Web API中完美配置Swagger文档
前端·.netcore
m0_748232925 小时前
【前端】Node.js使用教程
前端·node.js·vim
hawleyHuo5 小时前
umi 能适配 taro组件?
前端·前端框架
web130933203986 小时前
[JAVA Web] 02_第二章 HTML&CSS
java·前端·html
黑客呀6 小时前
Go Web开发之Revel - 网页请求处理流程
开发语言·前端·web安全·golang·系统安全