美文网首页
SVG 实现拟物态圆环进度条

SVG 实现拟物态圆环进度条

作者: 苏苏哇哈哈 | 来源:发表于2022-12-12 00:01 被阅读0次

    前言

    👏SVG 实现拟物态圆环进度条,速速来Get吧~

    🥇文末分享源代码。记得点赞+关注+收藏!

    1.实现效果

    在这里插入图片描述

    2.实现步骤

    • 定义一个圆角矩形作为父容器,背景色为--bg
    在这里插入图片描述
    --bg: #edf1f5;
    
    <div class="container"></div>
    
    .container {
     padding: 30px;
     border-radius: 20px;
     background: var(--bg);
     box-shadow: 0px 20px 30px rgba(100, 131, 177, 0.2);
     border: 1px solid #fff;
    }
    
    • 父容器添加一个正方形,宽高w
    在这里插入图片描述
    /* 宽度 */
     --w: 200px;
    
     <div class="container-box" ></div>
    
    .container-box {
      position: relative;
      width: var(--w);
      height: var(--w);
      border: 1px solid red;
    }
    
    • 在container-box容器中添加一个圆形,宽高为100%,基于父元素absolute定位,left为0,top为0,添加box-shadow阴影,定义阴影变量--back-bg和--back-shadow
    在这里插入图片描述
    --back-bg: #c5ccda;
    --back-shadow: #ffffff;
    
    <div class="circle-outer"></div>
    
    .circle-outer {
      position: relative;
      width: 100%;
      height: 100%;
      border-radius: 50%;
      box-shadow: 6px 6px 8px var(--back-bg), -6px -6px 8px var(--back-shadow);
      background: var(--bg);
    }
    
    • 为circle-outer添加前伪元素,用该伪元素实现一个圆形,定义css变量gap,表示该圆形与父元素之前的距离
    在这里插入图片描述
    /* 父容器gap*/
    --gap: 30px;
    
    • 那么可以算出circle-outer前伪元素的宽高为--inner,即calc(var(--w) - var(--gap))
    在这里插入图片描述
    /* 第二层圆形宽度 */
    --inner: calc(var(--w) - var(--gap));
    
    .circle-outer::before {
     content: "";
     position: absolute;
     left: 50%;
     top: 50%;
     transform: translate(-50%, -50%);
     border-radius: 50%;
     background: var(--bg);
     width: var(--inner);
     height: var(--inner);
     box-shadow: inset 8px 8px 10px var(--back-bg),
       inset -4px -4px 8px var(--back-shadow);
    }
    
    • 在container-box容器中添加svg,宽高为父元素的100%,基于父元素absolute定位

    SVG:

    可缩放矢量图形(Scalable Vector Graphics,SVG)基于 XML 标记语言,用于描述二维的矢量图形

    svg {
     position: absolute;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
      z-index: 1;
      //旋转90deg
      transform: rotate(-90deg);
    }
    
    • svg中添加defs

    defs:

    SVG 允许我们定义以后需要重复使用的图形元素。建议把所有需要再次使用的引用元素定义在defs元素里面。这样做可以增加 SVG 内容的易读性和无障碍。在defs元素中定义的图形元素不会直接呈现。你可以在你的视口的任意地方利用 <use>元素呈现这些元素。

    • defs中定义一个radialGradient

    radialGradient:
    radialGradient 用来定义径向渐变,以对图形元素进行填充或描边。

    • 定义两个颜色,表示radialGradient的色值,即环形的色值
    --ring-color1: #c1dfc4;
    --ring-color2: #3cba92;
    
    <defs>
     <radialGradient
        id="gradient"
        cx="50%"
        cy="50%"
        r="60%"
        fx="50%"
        fy="50%"
      >
        <stop offset="30%" stop-color="var(--ring-color2)" />
        <stop offset="100%" stop-color="var(--ring-color1)" />
      </radialGradient>
    </defs>
    
    • 在svg中添加circle,stroke为上述定义的radialGradient

    circle:
    <circle> SVG 元素是一个 SVG 的基本形状,用来创建圆,基于一个圆心和一个半径。
    cx 属性定义一个中心点的 x 轴坐标。
    cy 属性定义一个中心点的 y 轴坐标。
    r 属性用来定义圆的半径。

    <circle stroke="url(#gradient)" id="circle"></circle>
    
    • 这里我们先说明几个字段的意义

    stroke-width:
    stroke-width属性指定了当前对象的轮廓的宽度。它的默认值是1。如果使用了一个<percentage>,这个值代表当前视口的百分比。如果使用了0值,则将不绘制轮廓。

    stroke-dasharray:
    属性 stroke-dasharray 可控制用来描边的点划线的图案范式。作为一个外观属性,它也可以直接用作一个 CSS 样式表内部的属性。

    eg:

    stroke-dasharray = 20 表示:虚线长20,间距20,然后重复 虚线长20,间距20
    

    stroke-dashoffset:
    stroke-dashoffset 属性指定了 dash 模式到路径开始的距离,偏移的意思

    • 定义css变量stroke,表示stroke-width,定义--circle表示svg中circle的宽高,由下图可以得到--circle为calc(var(--inner) - var(--stroke))
    在这里插入图片描述
    /* 环形stroke宽度 */
    --stroke: 20px;
    /* svg环形宽度 */
    --circle: calc(var(--inner) - var(--stroke));
    
    • 为cricle添加样式,基于svg水平垂直居中,根据css变量设置cx,cy,r,stroke-dasharray为圆的周长,即3.14*圆的直径(--circle)
    • 这里先设置stroke-dashoffset为任意值,当stroke-dashoffset与stroke-dasharray相等的时候,将不显示内容,stroke-dashoffset设置的越小,显示内容越多,当为0时候,显示全部
    在这里插入图片描述
    svg circle {
     position: absolute;
     //设置垂直居中
     --z: calc(var(--w) / 2);
     --c: calc(var(--circle) / 2);
     transform: translate(
       calc(var(--z) - var(--c)),
       calc(var(--z) - var(--c))
     );
     cy: calc(var(--circle) / 2);
     cx: calc(var(--circle) / 2);
     r: calc(var(--circle) / 2);
     fill: none;
     stroke-linecap: round;
     stroke-dasharray: calc(3.14 * var(--circle));
     stroke-dashoffset: 250;
     stroke-width: var(--stroke);
    }
    
    • 为circle-outer添加后伪元素,用该伪元素实现一个圆形
    在这里插入图片描述
    • 那么可以算出circle-outer后伪元素的宽高为--center,即calc(var(--circle) - var(--stroke))
    在这里插入图片描述
    /* 第三层圆形宽度 */
    --center: calc(var(--circle) - var(--stroke));
    
    .circle-outer::after {
      content: "";
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
      border-radius: 50%;
      background: var(--bg);
      width: var(--center);
      height: var(--center);
      box-shadow: 6px 6px 8px var(--back-bg), -2px -2px 8px var(--back-shadow);
    }
    
    • 为container-box添加伪元素,显示当前百分比,标签内定义data-num,去掉辅助边框,有模有样了哈~
    在这里插入图片描述
    <div class="container-box" data-num="--" >
    ...
    </div>
    
     .container-box::after {
      content: attr(data-num);
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
      color: var(--text-aolor);
      font-size: 30px;
    }
    .container-box {
     - border: 1px solid red;
    }
    
    • 接下来,写一个定时器,实现进度条加载,就完成了啦~
    在这里插入图片描述
     const getData = () => {
      const circle = document.getElementById("circle");
      const box = document.getElementById("box");
      let perimeter = 3.14 * 150; //这是stroke-dasharray的值
      let i = 0;
      let timer = null;
      const loading = () => {
        if (i < 100) {
          i++;
          box.dataset.num = i + "%";
          let per = perimeter - (perimeter * i) / 100;
          circle.style.strokeDashoffset = per;
        } else {
          clearInterval(timer);
        }
      };
      loading();
      timer = setInterval(loading, 100);
    };
    getData();
    

    3.实现代码

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <title>151.SVG 实现拟物态圆环进度条</title>
      </head>
      <link rel="stylesheet" href="../common.css" />
      <style>
        body {
          background: var(--back-bg);
        }
        :root {
          --bg: #edf1f5;
          --back-bg: #c5ccda;
          --back-shadow: #ffffff;
          --ring-color1: #c1dfc4;
          --ring-color2: #3cba92;
          --text-aolor: #497241;
          /* 父容器宽度 */
          --w: 200px;
          /* 父容器gap*/
          --gap: 30px;
          /* 第二层圆形宽度 */
          --inner: calc(var(--w) - var(--gap));
          /* 环形stroke宽度 */
          --stroke: 20px;
          /* svg环形宽度 */
          --circle: calc(var(--inner) - var(--stroke));
          /* 第三层圆形宽度 */
          --center: calc(var(--circle) - var(--stroke));
        }
        .container {
          padding: 30px;
          border-radius: 20px;
          background: var(--bg);
          box-shadow: 0px 20px 30px rgba(100, 131, 177, 0.2);
          border: 1px solid #fff;
        }
        .container-box {
          position: relative;
          width: var(--w);
          height: var(--w);
        }
        .circle-outer {
          position: relative;
          width: 100%;
          height: 100%;
          border-radius: 50%;
          box-shadow: 6px 6px 8px var(--back-bg), -6px -6px 8px var(--back-shadow);
          background: var(--bg);
        }
        .circle-outer::before {
          content: "";
          position: absolute;
          left: 50%;
          top: 50%;
          transform: translate(-50%, -50%);
          border-radius: 50%;
          background: var(--bg);
          width: var(--inner);
          height: var(--inner);
          box-shadow: inset 8px 8px 10px var(--back-bg),
            inset -4px -4px 8px var(--back-shadow);
        }
        .circle-outer::after {
          content: "";
          position: absolute;
          left: 50%;
          top: 50%;
          transform: translate(-50%, -50%);
          border-radius: 50%;
          background: var(--bg);
          width: var(--center);
          height: var(--center);
          box-shadow: 6px 6px 8px var(--back-bg), -2px -2px 8px var(--back-shadow);
        }
        .container-box::after {
          content: attr(data-num);
          position: absolute;
          left: 50%;
          top: 50%;
          transform: translate(-50%, -50%);
          color: var(--text-aolor);
          font-size: 30px;
        }
        svg {
          position: absolute;
          left: 0;
          top: 0;
          width: 100%;
          height: 100%;
          z-index: 1;
          transform: rotate(-90deg);
        }
        svg circle {
         position: absolute;
          --z: calc(var(--w) / 2);
          --c: calc(var(--circle) / 2);
          transform: translate(
            calc(var(--z) - var(--c)),
            calc(var(--z) - var(--c))
          );
          cy: calc(var(--circle) / 2);
          cx: calc(var(--circle) / 2);
          r: calc(var(--circle) / 2);
          fill: none;
          stroke-linecap: round;
          /* 设置圆的stroke-dasharray和stroke-dashoffset,为圆的周长 */
          stroke-dasharray: calc(3.14 * var(--circle));
          stroke-dashoffset:calc(3.14 * var(--circle));
          stroke-width: var(--stroke);
        }
      </style>
      <body>
        <div class="container">
          <div class="container-box" data-num="--" id="box">
            <div class="circle-outer"></div>
            <svg>
              <defs>
                <radialGradient
                  id="gradient"
                  cx="50%"
                  cy="50%"
                  r="60%"
                  fx="50%"
                  fy="50%"
                >
                  <stop offset="30%" stop-color="var(--ring-color2)" />
                  <stop offset="100%" stop-color="var(--ring-color1)" />
                </radialGradient>
              </defs>
              <circle stroke="url(#gradient)" id="circle"></circle>
            </svg>
          </div>
        </div>
      </body>
      <script>
        const getData = () => {
          const circle = document.getElementById("circle");
          const box = document.getElementById("box");
          let perimeter = 3.14 * 150; //这是stroke-dasharray的值
          let i = 0;
          let timer = null;
          const loading = () => {
            if (i < 100) {
              i++;
              box.dataset.num = i + "%";
              let per = perimeter - (perimeter * i) / 100;
              circle.style.strokeDashoffset = per;
            } else {
              clearInterval(timer);
            }
          };
          loading();
          timer = setInterval(loading, 100);
        };
        getData();
      </script>
    </html>
    

    4.写在最后🍒

    看完本文如果觉得对你有一丢丢帮助,记得点赞+关注+收藏鸭 🍕
    更多相关内容,关注🍥苏苏的bug,🍡苏苏的github,🍪苏苏的码云~

    相关文章

      网友评论

          本文标题:SVG 实现拟物态圆环进度条

          本文链接:https://www.haomeiwen.com/subject/zazxqdtx.html