流動的SVG線條

AJie發表於2019-04-02

話不多說,我們先上效果圖:

流動的SVG線條

是不是很酷炫,這個效果是用SVG+CSS實現的,現在我們就來一起解構一下這個動畫的實現。


預備知識

需要掌握的幾個SVG的幾個標籤用法

  • <path>

    指令 引數 說明
    M x y 將畫筆移動到點(x,y)
    L x y 畫筆從當前的點繪製線段到點(x,y)
    H x 畫筆從當前的點繪製水平線段到點(x,y0)
    V y 畫筆從當前的點繪製豎直線段到點(x0,y)
    A rx ry x-axis-rotation large-arc-flag sweep-flag x y 畫筆從當前的點繪製一段圓弧到點(x,y)
    C x1 y1, x2 y2, x y 畫筆從當前的點繪製一段三次貝塞爾曲線到點(x,y)
    S x2 y2, x y 特殊版本的三次貝塞爾曲線(省略第一個控制點)
    Q x1 y1, x y 繪製二次貝塞爾曲線到點(x,y)
    T x y 特殊版本的二次貝塞爾曲線(省略控制點)
    Z 無引數 繪製閉合圖形,如果d屬性不指定Z命令,則繪製線段,而不是封閉圖形。

    path的指令一共有9種,但實現我們的動畫我們只運用了其中兩個:A和Z,所以我們這裡著重討論一下A指令!

    繪製圓弧指令:A rx ry x-axis-rotation large-arc-flag sweep-flag x y

    對於給定兩個點(一個起點一個終點)和一個半徑,可以畫4條弧線。這四條弧線可以按照兩種分法:

    1. 按角度分
      • 大於180°的
      • 小於180°的
    2. 按畫筆方向分
      • 順時針方向
      • 逆時針方向

流動的SVG線條

  • rx,ry 是弧的半長軸、半短軸長度

  • x-axis-rotation 是圓弧旋轉角度,是此段弧所在的半長軸與水平方向的夾角。正數代表順時針轉動的角度。

  • large-arc-flag 為1 表示大角度弧線,0 代表小角度弧線。

  • sweep-flag 為1代表從起點到終點弧線繞中心順時針方向,0 代表逆時針方向。

  • x,y 是弧終端座標。

  • <defs>

    SVG 允許我們定義以後需要重複使用的圖形元素。 建議把所有需要再次使用的引用元素定義在defs元素裡面。這樣做可以增加SVG內容的易讀性和可訪問性。 在defs元素中定義的圖形元素不會直接呈現。 你可以在你的視口的任意地方利用 ``元素呈現這些元素。——MDN

    這個標籤的用意簡單地說就是定義一個繪圖模板,以供我們可以在別處直接使用。

    在這裡我們可以定義我們的漸變元素:linearGradient。

  • <linearGradient>

    linearGradient元素用來定義線性漸變,用於圖形元素的填充或描邊。——MDN

    一個漸變上的顏色坡度,是用stop元素定義的。

    例如:

    <stop offset="5%" stop-color="#F60" />
    複製程式碼

    這行程式碼的意思是:在距離起點位置偏移5%的地方顏色為 #F60。

    我們在引用自己定義的漸變進行填充顏色設定或畫筆顏色設定時,僅需要用 url()的方式鏈入漸變的ID即可,具體如下:

    stroke="url(#linear)"
    複製程式碼

    這裡我們將畫筆的顏色設定為了ID為linear的漸變。

還有兩個屬性:

  • stroke-dasharray

    用於建立虛線:

    stroke-dasharray = '10'
    stroke-dasharray = '10, 10'
    stroke-dasharray = '10, 10, 5, 5'
    複製程式碼

流動的SVG線條

繪製虛線: 一個引數時: 表示一段虛線長度和每段虛線之間的間距

兩個引數或者多個引數時:一個表示長度,一個表示間距

  • stroke-dashoffset

    stroke-dashoffset 屬性指定了dash模式到路徑開始的距離

    如果使用了一個 百分比值, 那麼這個值就代表了當前viewport的一個百分比。

    值可以取為負值。——MDN

    簡單的講,stroke-dashoffset設定的是畫筆起點的偏移量,正值向左偏移。

具體實現

畫SVG路徑

筆者通過描點,將網上下載下來的一張蘋果圖描出了SVG,可能不大精確,但是應該夠用。這裡放一下我描點的圖:

流動的SVG線條

<path
  stroke-linecap="round"
  fill="none"
  stroke-width="10"
  stroke="url(#linear)"
  d="
M 197,148
A 87,87,0,0,0,79,214
A 282,282,0,0,0,148,438
A 54,54,0,0,0,212,448
A 87,87,0,0,1,288,448
A 54,54,0,0,0,352,438
A 282,282,0,0,0,407,350
A 87,87,0,0,1,413,189
A 87,87,0,0,0,303,148
A 141,141,0,0,1,197,148
Z
"
/>
<path
  stroke-linecap="round"
  fill="none"
  stroke="url(#linear)"
  stroke-width="10"
  d="
M 237,141
A 87,87,0,0,0,314,64
A 87,87,0,0,0,237,141
Z
"
/>
複製程式碼

並且我將我的畫筆顏色設定為即將編寫的漸變id。

定義漸變色

<defs>
  <linearGradient id="linear" x1="0%" y1="0%" x2="100%" y2="100%">
    <stop offset="0%" stop-color="rgb(98, 180, 76)" />
    <stop offset="20%" stop-color="rgb(242, 179, 61)" />
    <stop offset="40%" stop-color="rgb(238, 125, 55)" />
    <stop offset="60%" stop-color="rgb(205, 58, 71)" />
    <stop offset="80%" stop-color="rgb(142, 61, 140)" />
    <stop offset="100%" stop-color="rgb(39, 155, 213)" />
  </linearGradient>
</defs>
複製程式碼

為了顯示為彩色,我設定了六個漸變顏色節點。

值得注意的是:漸變顏色的即便應用在畫筆上,他的填色還是按照面的顏色填充來填充的。就拿這個例子來說,我在linearGradient上定義了x1="0%" y1="0%" x2="100%" y2="100%",使頁面從左上角到右下角顏色顯示為漸變,而不是路徑的起點到終點的漸變,所以我們定義漸變在0%和100%的顏色並不需要一樣。(實際上我們也不能定義從路徑的起點到終點的漸變)

JavaScript獲取路徑長度

let pathLength = document.querySelectorAll('path');
pathLength.forEach((item, index) => {
  let itemLength = Math.ceil(item.getTotalLength());
  console.log(itemLength);
});
複製程式碼

流動的SVG線條

這裡我們運用到了一個JavaScript API:getTotalLength(),用於獲取路徑長度,方便我們設定偏移量。

設定動畫

path {
  animation: dash 5s linear forwards;
}
path:nth-child(2) {
  stroke-dasharray: 1162;
  stroke-dashoffset: -1162;
}
path:nth-child(3){
  stroke-dasharray: 236;
  stroke-dashoffset: -236;
}
@keyframes dash {
  to {
    stroke-dashoffset: 0;
  }
}
複製程式碼

我們的動畫使從無到有的一個過程,所以我們將虛線長度和每段虛線之間的間距*都設定為路徑的長度。

在這種情況下我們顯示的應該是完整的蘋果logo。

當我們設定stroke-dashoffset為路徑長度的時候,那麼我們就將畫面的虛線部分完全隱藏在了畫筆起點之前。整個畫面將是空空如也!

這時候我們為兩個path新增動畫,讓其在5s之內勻速的改變stroke-dashoffset至0,那麼呈現在我們眼前的就是如前面的動圖所示,一個動態繪製logo的過程。

類似動畫的其他實現

先上效果圖:

流動的SVG線條

實現程式碼如下:

<svg
xmlns="http://www.w3.org/2000/svg"
width="200px"
height="164.50px"
class="icon"
version="1.1"
viewBox="0 0 1245 1024"
id="logo-pic"
>
<defs>
  <linearGradient id="linear" x1="0%" y1="0%" x2="100%" y2="0%">
    <stop offset="0%" stop-color="#05a" />
    <stop offset="100%" stop-color="#0a5" />
  </linearGradient>
</defs>
<path
  fill="url(#linear)"
  d="M870.953514 333.768649c-209.781622 0-374.867027 145.020541-374.867028 322.836756s165.223784 322.836757 374.867028 322.836757a548.116757 548.116757 0 0 0 132.289729-22.417297L1124.185946 1024l-33.210811-110.702703C1179.537297 845.768649 1245.405405 756.92973 1245.405405 656.605405c0-177.816216-176.294054-322.836757-374.451891-322.836756z m-121.496217 267.208648A47.463784 47.463784 0 0 1 705.72973 556.419459 47.740541 47.740541 0 0 1 749.457297 512c33.349189 0 55.351351 22.278919 55.351352 44.557838s-22.002162 44.419459-55.351352 44.419459z m242.438919 0a47.325405 47.325405 0 0 1-43.727567-44.557838A47.602162 47.602162 0 0 1 991.896216 512c33.072432 0 55.351351 22.278919 55.351352 44.557838s-22.278919 44.419459-55.351352 44.419459z"
/>
<path
  fill="url(#linear)"
  d="M440.735135 0C198.434595 0 0 166.054054 0 378.326486c0 122.188108 66.006486 222.512432 176.432432 300.41946l-44.142702 133.811892 154.153513-77.907027c55.351351 10.931892 99.355676 22.278919 154.430271 22.278919 13.837838 0 27.675676 0 41.513513-1.798919a334.045405 334.045405 0 0 1-13.837838-93.267027c0-193.72973 166.054054-352.034595 374.728649-352.034595a378.88 378.88 0 0 1 42.482162 2.629189C847.429189 133.12 657.574054 0 440.735135 0zM294.192432 296.683243a53.137297 53.137297 0 1 1 52.722163-53.137297 52.860541 52.860541 0 0 1-52.722163 53.137297z m314.395676 0a53.137297 53.137297 0 1 1 52.722162-53.137297A52.860541 52.860541 0 0 1 608.864865 296.683243z"
/>
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
width="100%"
height="2rem"
id="logo-name"
>
<text
  text-anchor="middle"
  x="50%"
  y="50%"
  class="logo logo-name-text-1"
>
  WeChat
</text>
<text
  text-anchor="middle"
  x="50%"
  y="50%"
  class="logo logo-name-text-2"
>
  WeChat
</text>
<text
  text-anchor="middle"
  x="50%"
  y="50%"
  class="logo logo-name-text-3"
>
  WeChat
</text>
</svg>
複製程式碼

值得注意的是,在SVG中文字也可以將邊框和填充分開來填色。所以這裡我們將文字的邊框設為最開始定義的漸變色,而填充設定為none。

#logo-pic {
  width: 75px;
  height: 75px;
}
.logo {
  font-size: 1.25rem;
  font-weight: bold;
  fill: none;
  stroke-width: 1px;
  stroke-dasharray: 30% 70%;
  animation: stroke 4.5s infinite linear;
}
.logo-name-text-1 {
  stroke: rgb(0, 169, 86);
}
.logo-name-text-2 {
  stroke: rgb(0, 127, 129);
  animation-delay: -1.5s;
}
.logo-name-text-3 {
  stroke: rgb(0, 86, 168);
  animation-delay: -3s;
}
@keyframes stroke {
  to {
    stroke-dashoffset: -100%;
  }
}
複製程式碼

筆者專門在 github 上建立了一個倉庫,用於記錄平時學習全棧開發中的技巧、難點、易錯點,歡迎大家點選下方連結瀏覽。如果覺得還不錯,就請給個小星星吧!?


2019/04/02

AJie

相關文章