css3動畫+svg實現(xiàn)水球進度條

背景

??最近在工作中遇到一個水球進度條,用svg繪制幾個波浪疊加動畫寫起來超簡單,6哇~
??文末分享源代碼。記得點贊+關注+收藏!

1.實現(xiàn)效果

demo145.gif

2.實現(xiàn)原理

2.1 邊框圓角漸變色

  • 我們都知道,實現(xiàn)一個邊框漸變色可以用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;
  }
  • 實現(xiàn)邊框圓角 漸變的方法蠻多的,這里說一下background-clip吧(缺點:內容背景無法透明)~

background-clip
規(guī)定背景的繪制區(qū)域

語法:

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實現(xiàn)波浪

2.2.1 SVG是什么

SVG:可縮放矢量圖形
可縮放矢量圖形(Scalable Vector Graphics,SVG)基于 XML 標記語言,用于描述二維的矢量圖形。
作為一個基于文本的開放網(wǎng)絡標準,SVG 能夠優(yōu)雅而簡潔地渲染不同大小的圖形,并和CSS、DOM、JavaScript 和 SMIL 等其他網(wǎng)絡標準無縫銜接。
本質上,SVG 相對于圖像,就好比 HTML 相對于文本。

和傳統(tǒng)的點陣圖像模式(如 JPEG 和 PNG)不同的是,SVG 格式提供的是矢量圖,這意味著它的圖像能夠被無限放大而不失真或降低質量,并且可以方便地修改內容,無需圖形編輯器。通過使用合適的庫進行配合,SVG 文件甚至可以隨時進行本地化。

兼容性:

image.png

2.2.2 SVG屬性

  • version: 用于指明 SVG 文檔遵循規(guī)范。它只允許在根元素svg 上使用。它純粹是一個說明,對渲染或處理沒有任何影響,雖然它接受任何數(shù)字,但是只有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 個參數(shù)的列表 min-x, min-y, width and height,以空格或者逗號分隔開,在用戶空間中指定一個矩形區(qū)域映射到給定的元素。 width 或者 height 的值,小于或等于 0 的情況下,這個元素將不會被渲染出來。

2.2.3 SVG路徑

path元素是 SVG基本形狀中最強大的一個。你可以用它創(chuàng)建線條,曲線,弧形等等。
path 只需要設定很少的點,就可以創(chuàng)建平滑流暢的線條(比如曲線)。
雖然polyline元素也能實現(xiàn)類似的效果,但是必須設置大量的點(點越密集,越接近連續(xù),看起來越平滑流暢),并且這種做法不能夠放大(放大后,點的離散更明顯)

path 標簽用來定義路徑,path 元素的形狀是通過屬性d定義的,屬性d的值是一個“命令 + 參數(shù)”的序列
下面的命令可用于路徑數(shù)據(jù):(所有命令均可小寫。大寫表示絕對定位,小寫表示相對定位)

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 繪制波浪
  • 可以借助一些專業(yè)工具來完成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.實現(xiàn)步驟

3.1 基本樣式

  • 畫一個邊框漸變的圓環(huán),為外圓
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距離,假設現(xiàn)在設置為-70%,看看是啥樣的
31.gif
  • 設置外圓溢出隱藏,overflow:hidden,可以看出水球由下半部的正方形+上半部分的svg波浪組成
image.png
  • 兩者連接處可以看見一條明顯的線條,我們可以通過對波浪1設置margin-bottom:-2px來解決
image.png
  • 按照上述2.3節(jié)所述,加上波動動畫,有點模樣了,6哇
41.gif

3.2 按照百分比展示

  • 首頁,改變內圓的bottom位置,我們試著將內圓移出外圓所見范圍(調試過程中,將外圓的溢出先去掉)
image.png
  • 將內圓bottom改完-128%(具體數(shù)值根據(jù)你繪制的svg寬高決定,可自行調試),即可將內圓移出外圓所見范圍,試想一下,百分之0的時候,內圓不可見
  • 當百分比改變的時候,0%對應bottom為-128%,10%對應為-128+10=118%;50%對應-128+50=78%;依次類推可得到一個公式:
bottom數(shù)值=calc(-128% + 當前百分比)
  • 或者我們不改變bottom數(shù)值,對該內圓進行y方向向上的偏移,0%對應偏移量為0,10%對應-10%,50%對應-50%;依次類推可得到一個公式:
transform數(shù)值=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.實現(xiàn)代碼

<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,??蘇蘇的碼云~
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容