使用 SVG 製做加載動畫

最近咱們設計師反饋,他想要作以下的一個加載動畫。可是要麼效果好的導出的 GIF 體積特別大,看了下有 8M 多了,要麼體積小的 GIF 效果又特別不清楚。而後我看了下效果,發現其實用 SVG 動畫來實現應該比較簡單,因而就和設計師要了一下原始的稿子導出成 SVG 後處理了下。css

因爲 SF 不支持插入視頻,你們能夠訪問騰訊視頻地址 https://v.qq.com/txp/iframe/p... 查看設計師預期的動效視頻。html

將 AE 動效稿子轉成 SVG 動畫的話 Airbnb 有出過一款 Lottie 的工具。經過它的 AE 插件 Bodymovin 可以以 JSON 的形式導出動畫信息和素材。而後在網頁上使用 bodymovin.js 動畫播放庫載入該 JSON 素材便可完成動效的轉換。具體的使用教程能夠參考 Youtube 視頻《How to export an animation with Bodymovin》web

使用 Bodymovin 是真的很是方便,不過介於設計師須要的效果比較簡單,爲了這個效果而每次去加載一個幾十KB的基礎庫和JSON文件實在是沒有必要。因此我這裏就基於 SVG + CSS 動畫來實現了下,最終的效果以下。最終體積也就 6KB,gzip 後會更小。
下面就來跟着我一塊一步步的實現它吧!這裏我不會特別詳細的描述每一步的基本原理,若是你們想了解 SVG 動畫的基礎知識的話能夠先看看我以前寫的文章《SVG 動畫實踐》ide

動畫拆分分析

經過觀察發現該動畫主要用到了平移、旋轉、透明度,寬度和顏色等屬性變化等動畫效果。這些均可以經過 CSS3 動畫來實現,剩下的咱們須要對這些動畫進行拆分,先分別實現它們。最後將他們組合,經過必定的時間配合實現完整的效果。svg

在這裏我將該動效最終拆分紅了如下幾個部分:工具

  • 外圈的波紋效果post

    • 外圈1的波紋效果
    • 外圈2的波紋效果
    • 外圈3的波紋效果
  • 主體的伸展運動優化

    • 主體綠色部分的平移伸展動畫

      • 主體綠色部分上的平移伸展
      • 主體綠色部分下的平移伸展
    • 主體白色圓球的漸隱效果
    • 主體藍色部分的平移伸展
    • 主體白色橫條的漸隱效果
    • 主體部分的自轉
  • 藍球的公轉效果

每個單獨的動畫效果咱們都須要對其進行處理,因此咱們須要對導出的 SVG 進行元素的整理,將咱們須要進行操做的元素進行分組標記。因爲 Sketch 導出的 SVG 文件會帶有比較多的冗餘元素,因此我通常會在手工處理 SVG 以前在走一遍 svgo 這類工具對內容進行優化。這裏推薦張鑫旭老師寫的 SVG 在線壓縮合並工具,直接粘貼 SVG 代碼過去便可,很是簡單。下面是 SVG 總體結構的示意代碼。網站

<svg width="552px" height="552px" viewBox="0 0 552 552" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <!--外圈-->
  <g id="track-list">
    <!--外圈1-->
    <circle id="track-circle-1" />
    <!--外圈2-->
    <circle id="track-circle-2" />
    <!--外圈3-->
    <circle id="track-circle-3" />
  </g>
  <!--中間主體-->
  <g id="main">
    <!--主體綠色部分下-->
    <g id="bottom-triangel">
      <path d="..." />
    </g>
    <!--主體綠色部分上-->
    <g id="top-triangel">
      <path id="shadow" d="..." />
      <path d="..." />
      <!--主體白色圓球-->
      <circle id="white-ball" />
    </g>
    <!--主體藍色部分-->
    <g id="right-triangel">
      <path d="..." />
      <!--主體白色橫條-->
      <rect id="white-line" />
    </g>
   </g>
   <!--外圈藍球-->
   <circle id="blue-ball" />
</svg>

能夠看到我對 SVG 內的元素進行了從新的整理,將須要操做的元素都加上了 id 屬性,方便後續直接使用 CSS 選擇器選擇對象進行操做。另外全部須要一塊進行操做的元素也都使用 <g /> 分組標籤進行了包裹。

外圈的波紋效果

外圈的波紋效果本質上就是圓的半徑慢慢放大,效果裏還伴隨了圓的邊框變窄的一個過程。其中三個圓最內層的那個是不須要動的,只須要動後面兩個便可。從設計稿中拿到結束幀的狀態以後這個動畫作起來就比較容易了。

#track-circle-2 {
  animation-name: wave1;
  animation-timing-function: ease-in-out;
  animation-duration: 6s;
  animation-iteration-count: infinite;
}

#track-circle-3 {
  animation-name: wave2;
  animation-timing-function: ease-in-out;
  animation-duration: 6s;
  animation-iteration-count: infinite;
}
@keyframes wave1 {
  50% {
    stroke-width: 4;
    r: 219px;
  }
}

@keyframes wave2 {
  50% {
    stroke-width: 3;
    r: 274.5px;
  }
}

預覽地址: https://code.h5jun.com/xovew/...

主體的伸展運動

這塊是整個裏面比較複雜的一部分了,不過經過拆分,咱們發現實現起來也比較簡單,先實現內部元素的平移,而後再補充上總體的自轉效果便可。平移這塊沒有什麼多說的,惟一麻煩的就是經過起始幀和結束幀的位置計算出須要移動的距離而已。如圖最終白線標記的位置就是咱們須要的平移位置啦。

#top-triangel { animation: topmove ease-in-out 2s infinite; }
#shadow { animation: shadowhide linear 2s infinite; }
#bottom-triangel { animation: bottommove ease-in-out 2s infinite; }
#right-triangel { animation-name: rightmove ease-in-out 2s infinite; }
@keyframes topmove {
  from, to { transform: translate(0, 0); }
  50% { transform: translate(-31px, -30px); }
}
@keyframes bottommove {
  from, to { transform: translate(0, 0); }
  50% { transform: translate(-31px, 30px); }
}
@keyframes rightmove {
  from, to { transform: translate(0); }
  50% { transform: translate(29px); }
}
@keyframes shadowhide {
  30%, 70% { opacity: 0; }
}

對了,別忘記咱們剛纔的動畫拆分裏還有白色圓球和白色橫條的漸隱效果。漸隱效果可使用 opacity 透明度來實現,不過這裏除了漸隱以外還有一個大小的變化,可能使用呼吸效果來表述會更合適一點。圓的大小就是修改半徑,橫條的大小咱們直接修改寬度就能夠了。

#white-ball { animation: balltransparent ease-in-out 2s infinite; }
#white-line { animation: linetransparent ease-in-out 2s infinite; }
@keyframes balltransparent {
  50% {
    opacity: 0;
    transform: scale(0);
  }
}
@keyframes linetransparent {
  50% {
    opacity: 0;
    width: 0px;
  }
}

根據剛纔的動畫拆分,主體部分咱們就還剩下一個自轉沒有實現了。在作這一部分的時候須要注意兩點。第一,旋轉默認是基於 SVG 畫布的左上角進行旋轉的,自轉的話通常都是基於中心旋轉,因此必定要記得設置 transform-origin 爲中心點。第二,Sketch 導出的 SVG 會存在大量的 translate() 平移屬性操做,有多是最開始設計師畫的時候是在某個位置,後來以爲不合適進行了移動,在 SVG 裏就會以平移變換體現出來。這個時候若是咱們直接使用 transform 進行變換的話其實是會複寫掉它們本來的平移的,這樣就致使了以前設置的旋轉圓心不正確的問題。

因此這種狀況下須要使用聯合變換,將以前的平移變換補充到 CSS 的變換中來就能夠了,這也是爲何代碼中會多出兩個 translate() 的緣由。

#main {
  animation: mainrotate linear 6s infinite;
  transform-origin: center center;
}

@keyframes mainrotate {
  from {
    transform: translate(0, 0) rotateZ(0deg) translate(-72px, -42px);
  }
  to {
    transform: translate(0, 0) rotateZ(360deg) translate(-72px, -42px);
  }
}

下面就是最終的實現效果。怎麼樣,是否是感受已經離勝利不遠了!

預覽地址: https://code.h5jun.com/dahag/...

藍球的公轉效果

動畫拆分裏的最後一步就是藍球的公轉效果了。從上文咱們知道,旋轉咱們是能夠設置旋轉圓心的。自轉是圍繞本身轉的,因此旋轉圓心是本身的中心,公轉則是圍繞太陽轉的,因此旋轉圓心是太陽的圓心,對應到咱們的動效裏其實就是整個畫布的中心。

在這裏我還使用了 CSS 表達角度的另一個單位 turn,它表示的是圈數,轉 360° 就表示 1turn。除了 turn 以外,CSS 角度還有 grad 梯度和 rad 弧度這兩個單位。grad 則是將一個圓劃分紅了400等分,轉 360° 就表示 400grad。而 rad 弧度則和咱們數學上的弧度表示基本一致,一個圓總共是 2πrad

#blue-ball {
  animation: spin linear 6s infinite;
  transform-origin: center center;
}

@keyframes spin {
  from { transform: rotate(0turn); }
  to { transform: rotate(1turn); }
}

預覽地址: https://code.h5jun.com/kiqi/e...

後記

最後將上面的代碼拼湊起來就能夠實現文章開頭的動畫效果了,是否是還挺簡單的。另外在 SVG 標籤中也支持內嵌 <style><script> 標籤,因此咱們能夠直接將樣式內嵌在 SVG 文件中,這樣咱們就能夠和引用 GIF 同樣直接使用 <img> 或者背景圖片的形式使用 SVG 而不須要其餘負擔,在一些不支持內嵌樣式的 Markdown 網站好比 Github 中效果奇佳哦!

相關文章
相關標籤/搜索