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

背景

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

1.实现效果

demo145.gif

2.实现原理

2.1 边框圆角渐变色

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

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

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

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

语法:

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

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

语法:

background-origin: padding-box|border-box|content-box;
描述
border-box 背景图像边界框的相对位置
padding-box 背景图像填充框的相对位置
content-box 背景图像的相对位置的内容框
111.gif
  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%;
    }
  }

2.2 svg实现波浪

2.2.1 SVG是什么

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

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

兼容性:

image.png

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 固定值
  • 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
2.2.3.1 借助一些工具来帮助我们进行绘制

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

image.png

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

image.png

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

image.png

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

image.png
2.2.3.2 绘制波浪
  • 可以借助一些专业工具来完成svg的绘制
image.png
 <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>
  • 在class样式中为其添加填充颜色,fill相当于css中的background;stroke相当于 css 中的 border-color。
image.png
div{
     fill: #a0edff;
     stroke: orange;
}

2.3 波浪波动动画

  • 绘制三个一模一样的波浪,设置不同的填充色,通过absolute定位在相同位置。
  • 设置不同的x轴方向的偏移量,不同的z-index层级,使波浪错落分布。
image.png
  • 添加动画,x偏移量变化,波浪1与波浪3设置x偏移由-50%--->0%,通过不同的动画时间,使得分布错乱。
  • 波浪2设置x偏移量由0%--->-50%。
21.gif
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);
   }
 }

3.实现步骤

3.1 基本样式

  • 画一个边框渐变的圆环,为外圆
image.png
<div class="box-inner"></div>
 .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; 溢出隐藏 */
 }
  • 外圆内画一个圆,基于外圆absolute定位,left为0,bottom为0,为内圆;
image.png
<div class="box-inner">
    <div class="inner"></div>
</div>
  .inner {
    position: relative;
    width: 100%;
    height: 100%;
    position: absolute;
    left: 0;
    bottom: -128%;
    background-color: #a0edff;
  }
  • 内圆内绘制三个svg波浪,基于内圆absolute定位,left为0,bottom为100%,位于内圆的最上方。由于要对其进行x方向的50%进行偏移,将svg的大小设置为该内圆宽度的2倍
image.png
  • 重新设置内圆的bottom距离,假设现在设置为-70%,看看是啥样的
31.gif
  • 设置外圆溢出隐藏,overflow:hidden,可以看出水球由下半部的正方形+上半部分的svg波浪组成
image.png
  • 两者连接处可以看见一条明显的线条,我们可以通过对波浪1设置margin-bottom:-2px来解决
image.png
  • 按照上述2.3节所述,加上波动动画,有点模样了,6哇
41.gif

3.2 按照百分比展示

  • 首页,改变内圆的bottom位置,我们试着将内圆移出外圆所见范围(调试过程中,将外圆的溢出先去掉)
image.png
  • 将内圆bottom改完-128%(具体数值根据你绘制的svg宽高决定,可自行调试),即可将内圆移出外圆所见范围,试想一下,百分之0的时候,内圆不可见
  • 当百分比改变的时候,0%对应bottom为-128%,10%对应为-128+10=118%;50%对应-128+50=78%;依次类推可得到一个公式:
bottom数值=calc(-128% + 当前百分比)
  • 或者我们不改变bottom数值,对该内圆进行y方向向上的偏移,0%对应偏移量为0,10%对应-10%,50%对应-50%;依次类推可得到一个公式:
transform数值=translateY(calc(0% - 当前百分比));
  • 为内圆设置行内样式,定义var变量表示当前百分比


    51.gif
 <div class="inner" style="--per:50%" id="box"></div>
.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)));*/
 }

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>
: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;
  }
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();

5.写在最后🍒

看完本文如果觉得对你有一丢丢帮助,记得点赞+关注+收藏鸭 🍕
更多相关内容,关注🍥苏苏的bug,🍡苏苏的github,🍪苏苏的码云~
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • CSS编码技巧 尽量减少代码重复 多种尺寸的按钮 使用em、rem作为边距的单位,可以让按钮尺寸随着于字号进行缩放...
    alfalfaw阅读 276评论 0 0
  • transition用于给元素属性变化添加过渡效果,使变化更加流畅自然。基本语法如下:div { transit...
    limengzhe阅读 2,606评论 0 2
  • 要实现的效果图如下 svg 语法学习 可以参考https://developer.mozilla.org/zh-C...
    冰落寞成阅读 1,933评论 0 1
  • 转载请声明 原文链接 关注公众号获取更多资讯 这篇文章主要总结H5的一些新增的功能以及一些基础归纳,这里只是一个提...
    程序员poetry阅读 9,057评论 22 225
  • 1.灵活的背景定位 方法一:calc() 请不要忘记在 calc() 函数 内部的 - 和 + 运算符的两侧各加 ...
    overflow_hidden阅读 1,693评论 2 0