• css3动画+svg实现水球进度条


    背景

    👏最近在工作中遇到一个水球进度条,用svg绘制几个波浪叠加动画写起来超简单,6哇~
    🥇文末分享源代码。记得点赞+关注+收藏!

    1.实现效果

    在这里插入图片描述

    2.实现原理

    2.1 边框圆角渐变色

    • 我们都知道,实现一个边框渐变色可以用border-image,但是border-image不支持圆角

    border-image
    border-image CSS 属性允许在元素的边框上绘制图像。这使得绘制复杂的外观组件更加简单,也不用在某些情况下使用九宫格了。使用 border-image 时,其将会替换掉border-style 属性所设置的边框样式。

    在这里插入图片描述

      div{
        width: 200px;
        height: 80px;
        border: 2px solid;
        border-image: linear-gradient(180deg, red, orange) 1;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 实现边框圆角 渐变的方法蛮多的,这里说一下background-clip吧(缺点:内容背景无法透明)~

    background-clip
    规定背景的绘制区域

    语法:

    background-clip: border-box|padding-box|content-box;
    
    • 1
    描述
    border-box背景被裁剪到边框盒
    padding-box背景被裁剪到内边距框
    content-box背景被裁剪到内容框

    background-origin:
    background-Origin属性指定background-position属性应该是相对位置。
    注意如果背景图像background-attachment是"固定",这个属性没有任何效果。

    语法:

    background-origin: padding-box|border-box|content-box;
    
    • 1
    描述
    border-box背景图像边界框的相对位置
    padding-box背景图像填充框的相对位置
    content-box背景图像的相对位置的内容框

    在这里插入图片描述

    :root {
        --bg: #222;
     }
      div {
        width: 100px;
        height: 100px;
        border: 2px solid transparent;
        background-image: linear-gradient(var(--bg), var(--bg)),
          linear-gradient(180deg, red, orange);
        /* 前一个为内容背景色,后面为边框渐变色 */
        background-origin: border-box;
        background-clip: content-box, border-box;
        border-radius: 0px;
        animation: 2s toBorder linear infinite alternate;
      }
    
      @keyframes toBorder {
        100% {
          border-radius: 50%;
        }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    2.2 svg实现波浪

    2.2.1 SVG是什么

    SVG:可缩放矢量图形
    可缩放矢量图形(Scalable Vector Graphics,SVG)基于 XML 标记语言,用于描述二维的矢量图形。
    作为一个基于文本的开放网络标准,SVG 能够优雅而简洁地渲染不同大小的图形,并和CSS、DOM、JavaScript 和 SMIL 等其他网络标准无缝衔接。
    本质上,SVG 相对于图像,就好比 HTML 相对于文本。

    和传统的点阵图像模式(如 JPEG 和 PNG)不同的是,SVG 格式提供的是矢量图,这意味着它的图像能够被无限放大而不失真或降低质量,并且可以方便地修改内容,无需图形编辑器。通过使用合适的库进行配合,SVG 文件甚至可以随时进行本地化。

    兼容性:

    在这里插入图片描述

    2.2.2 SVG属性
    • version: 用于指明 SVG 文档遵循规范。它只允许在根元素svg 上使用。它纯粹是一个说明,对渲染或处理没有任何影响,虽然它接受任何数字,但是只有1.0 和 1.1.这两个有效的选择。
    • 命名空间:
    xmlns:http://www.w3.org/2000/svg 固定值
    xmlns:xlink:http://www.w3.org/1999/xlink 固定值
    xml:space:preserve 固定值
    
    • 1
    • 2
    • 3
    • class:样式名称
    • width | height: 定义 svg 画布的大小
    • viewbox: viewBox 属性允许指定一个给定的一组图形伸展以适应特定的容器元素。viewBox 属性的值是一个包含 4 个参数的列表 min-x, min-y, width and height,以空格或者逗号分隔开,在用户空间中指定一个矩形区域映射到给定的元素。 width 或者 height 的值,小于或等于 0 的情况下,这个元素将不会被渲染出来。
    2.2.3 SVG路径

    path元素是 SVG基本形状中最强大的一个。你可以用它创建线条,曲线,弧形等等。
    path 只需要设定很少的点,就可以创建平滑流畅的线条(比如曲线)。
    虽然polyline元素也能实现类似的效果,但是必须设置大量的点(点越密集,越接近连续,看起来越平滑流畅),并且这种做法不能够放大(放大后,点的离散更明显)

    path 标签用来定义路径,path 元素的形状是通过属性d定义的,属性d的值是一个“命令 + 参数”的序列
    下面的命令可用于路径数据:(所有命令均可小写。大写表示绝对定位,小写表示相对定位)

    M = moveto
    L = lineto
    H = horizontal lineto
    V = vertical lineto
    C = curveto
    S = smooth curveto
    Q = quadratic Belzier curve
    T = smooth quadratic Belzier curveto
    A = elliptical Arc
    Z = closepath
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    2.2.3.1 借助一些工具来帮助我们进行绘制

    1.在线绘制svg波浪:https://getwaves.io/

    在这里插入图片描述

    2.在线绘制svg波浪:https://fffuel.co/sssurf/

    在这里插入图片描述

    3.在线绘制svg波浪:https://svgwave.in/

    在这里插入图片描述

    在线查看svg:https://c.runoob.com/more/svgeditor/

    在这里插入图片描述

    2.2.3.2 绘制波浪
    • 可以借助一些专业工具来完成svg的绘制

    在这里插入图片描述

     <svg xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 600 140" class="box-waves">
       <path d="M 0 70 Q 75 20,150 70 T 300 70 T 450 70 T 600 70 L 600 140 L 0 140 L 0 70Z">
        </path>
      </svg>
    
    • 1
    • 2
    • 3
    • 4
    • 在class样式中为其添加填充颜色,fill相当于css中的background;stroke相当于 css 中的 border-color。

    在这里插入图片描述

    div{
    	 fill: #a0edff;
    	 stroke: orange;
    }
    
    • 1
    • 2
    • 3
    • 4

    2.3 波浪波动动画

    • 绘制三个一模一样的波浪,设置不同的填充色,通过absolute定位在相同位置。
    • 设置不同的x轴方向的偏移量,不同的z-index层级,使波浪错落分布。

    在这里插入图片描述

    • 添加动画,x偏移量变化,波浪1与波浪3设置x偏移由-50%—>0%,通过不同的动画时间,使得分布错乱。
    • 波浪2设置x偏移量由0%—>-50%。

    在这里插入图片描述

    svg:nth-child(1) {
      fill: #a0edff;
       transform: translate(-50%, 0);
       z-index: 3;
       animation: wave-move1 1.5s linear infinite;
     }
    
    svg:nth-child(2) {
      fill: rgba(40, 187, 255, 0.5);
       transform: translate(0, 0);
       z-index: 2;
       animation: wave-move2 3s linear infinite;
     }
    
    svg:nth-child(3) {
      fill: #2084cc;
       transform: translate(-50%, 0);
       z-index: 1;
       animation: wave-move1 3s linear infinite;
     }
     
    @keyframes wave-move1 {
       100% {
         transform: translate(0, 0);
       }
     }
    
     @keyframes wave-move2 {
       100% {
         transform: translate(-50%, 0);
       }
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    3.实现步骤

    3.1 基本样式

    • 画一个边框渐变的圆环,为外圆

    在这里插入图片描述

    <div class="box-inner"></div>
    
    • 1
     .box-inner {
       width: 200px;
       height: 200px;
       box-shadow: 0px 2px 7px 0px #238fdb;
       border-radius: 50%;
       position: relative;
       border: 2px solid transparent;
       background-image: linear-gradient(#021f40, #021f40),
         linear-gradient(180deg, rgba(36, 144, 220, 0.41), rgba(37, 147, 225, 1));
       background-origin: border-box;
       background-clip: content-box, border-box;
      /* overflow: hidden; 溢出隐藏 */
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 外圆内画一个圆,基于外圆absolute定位,left为0,bottom为0,为内圆;

    在这里插入图片描述

    <div class="box-inner">
    	<div class="inner"></div>
    </div>
    
    • 1
    • 2
    • 3
      .inner {
        position: relative;
        width: 100%;
        height: 100%;
        position: absolute;
        left: 0;
        bottom: -128%;
        background-color: #a0edff;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 内圆内绘制三个svg波浪,基于内圆absolute定位,left为0,bottom为100%,位于内圆的最上方。由于要对其进行x方向的50%进行偏移,将svg的大小设置为该内圆宽度的2倍

    在这里插入图片描述

    • 重新设置内圆的bottom距离,假设现在设置为-70%,看看是啥样的

    在这里插入图片描述

    • 设置外圆溢出隐藏,overflow:hidden,可以看出水球由下半部的正方形+上半部分的svg波浪组成

    在这里插入图片描述

    • 两者连接处可以看见一条明显的线条,我们可以通过对波浪1设置margin-bottom:-2px来解决

    在这里插入图片描述

    • 按照上述2.3节所述,加上波动动画,有点模样了,6哇

    在这里插入图片描述

    3.2 按照百分比展示

    • 首页,改变内圆的bottom位置,我们试着将内圆移出外圆所见范围(调试过程中,将外圆的溢出先去掉)

    在这里插入图片描述

    • 将内圆bottom改完-128%(具体数值根据你绘制的svg宽高决定,可自行调试),即可将内圆移出外圆所见范围,试想一下,百分之0的时候,内圆不可见
    • 当百分比改变的时候,0%对应bottom为-128%,10%对应为-128+10=118%;50%对应-128+50=78%;依次类推可得到一个公式:
    bottom数值=calc(-128% + 当前百分比)
    
    • 1
    • 或者我们不改变bottom数值,对该内圆进行y方向向上的偏移,0%对应偏移量为0,10%对应-10%,50%对应-50%;依次类推可得到一个公式:
    transform数值=translateY(calc(0% - 当前百分比));
    
    • 1
    • 为内圆设置行内样式,定义var变量表示当前百分比
      在这里插入图片描述
     <div class="inner" style="--per:50%" id="box"></div>
    
    • 1
    .inner {
       width: 100%;
       height: 100%;
       position: absolute;
       left: 0;
       background-color: #a0edff;
       /* 直接对bottom操作 */
       bottom: calc(-128% + var(--per));
       /* 或者y方向偏移
       transform: translateY(calc(0% - var(--per)));*/
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    4.实现代码

    <div class="box flex-row j_c">
      <div class="box-inner">
         <div class="inner" style="--per:0%" id="box">
           <svg xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 600 140" class="box-waves">
             <path d="M 0 70 Q 75 20,150 70 T 300 70 T 450 70 T 600 70 L 600 140 L 0 140 L 0 70Z">
             </path>
           </svg>
           <svg xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 600 140" class="box-waves">
             <path d="M 0 70 Q 75 20,150 70 T 300 70 T 450 70 T 600 70 L 600 140 L 0 140 L 0 70Z">
             </path>
           </svg>
           <svg xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 600 140" class="box-waves">
             <path d="M 0 70 Q 75 20,150 70 T 300 70 T 450 70 T 600 70 L 600 140 L 0 140 L 0 70Z">
             </path>
           </svg>
         </div>
       </div>
       <div id="percentText" class="box-text">--%</div>
     </div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    :root {
        --bg: #222;
        --wave1: #a0edff;
        --wave2: rgba(40, 187, 255, 0.5);
        --wave3: #2084cc;
      }
    
      body {
        background: var(--bg);
      }
    
      .box {
        width: 650px;
        height: 300px;
        background: linear-gradient(270deg,
            rgba(29, 170, 210, 0) 0%,
            rgba(29, 170, 210, 0.1) 13%,
            rgba(29, 170, 210, 0.4) 49%,
            rgba(29, 170, 210, 0.1) 84%,
            rgba(29, 170, 210, 0) 100%);
        border: 1px solid;
        border-image: linear-gradient(270deg,
            rgba(81, 201, 232, 0),
            rgba(56, 187, 222, 1),
            rgba(30, 172, 212, 0)) 1 1;
        margin-bottom: 120px;
      }
    
      .box-inner {
        width: 200px;
        height: 200px;
        box-shadow: 0px 2px 7px 0px #238fdb;
        border-radius: 50%;
        position: relative;
        border: 2px solid transparent;
        background-image: linear-gradient(#021f40, #021f40),
          linear-gradient(180deg, rgba(36, 144, 220, 0.41), rgba(37, 147, 225, 1));
        background-origin: border-box;
        background-clip: content-box, border-box;
        overflow: hidden;
      }
    
      .inner {
        width: 100%;
        height: 100%;
        position: absolute;
        left: 0;
        background: #a0edff;
        /* 直接对bottom操作 */
        bottom: calc(-128% + var(--per));
      }
    
    
      .box-waves {
        position: absolute;
        left: 0;
        bottom: 100%;
        width: 200%;
        stroke: none;
      }
    
      .box-waves:nth-child(1) {
        fill: var(--wave1);
        transform: translate(-50%, 0);
        z-index: 3;
        animation: wave-move1 1.5s linear infinite;
        /* svg重合有一条线 */
        margin-bottom: -2px;
      }
    
      .box-waves:nth-child(2) {
        fill: var(--wave2);
        transform: translate(0, 0);
        z-index: 2;
        animation: wave-move2 3s linear infinite;
      }
    
      .box-waves:nth-child(3) {
        fill: var(--wave3);
        transform: translate(-50%, 0);
        z-index: 1;
        animation: wave-move1 3s linear infinite;
      }
    
    
      @keyframes wave-move1 {
        100% {
          transform: translate(0, 0);
        }
      }
    
      @keyframes wave-move2 {
        100% {
          transform: translate(-50%, 0);
        }
      }
    
      .box-text {
        font-size: 30px;
        font-weight: bold;
        width: 80px;
        margin-left: 20px;
        text-align: center;
        color: #7EEDFF;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    const getData = () => {
     const box = document.getElementById('box');
     const text = document.getElementById('percentText');
     let i = 0;
     let timer = null;
     const loading = () => {
       if (i < 100) {
         i++;
         box.style.setProperty('--per', i + '%'); // 设置CSS变量
         text.innerHTML = i + '%';
       } else {
         i = 0;
         clearInterval(timer);
         setTimeout(() => {
           text.innerHTML = '0%';
           box.style.setProperty('--per', '0%'); // 设置CSS变量
           timer = setInterval(loading, 1000);
         }, 2000);
       }
     }
     loading();
     timer = setInterval(loading, 1000);
    }
    getData();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    5.写在最后🍒

    看完本文如果觉得对你有一丢丢帮助,记得点赞+关注+收藏鸭 🍕
    更多相关内容,关注🍥苏苏的bug,🍡苏苏的github,🍪苏苏的码云~
  • 相关阅读:
    Python图像处理:局部直方图均衡化和自动色彩均衡化
    古代汉语(王力版)笔记 通论1-5
    react native使用5-搭建ios环境
    无胁科技-TVD每日漏洞情报-2022-8-4
    SpringBoot基础之声明式事务和切面事务和编程式事务
    阿里巴巴开源组件Easy Excel的Excel导入导出
    delete、drop、truncate三兄弟
    uniapp 表格组件,冻结首行首列
    鸽群优化算法(Pigeon-inspired Optimization algorithm, PIO)附matlab代码
    【FPGA教程案例74】基础操作4——基于Vivado的FPGA布局布线分析
  • 原文地址:https://blog.csdn.net/qq_48085286/article/details/127742317