SVG-動畫的一把好手

Apollozz發表於2018-12-05

理解SVG的大小和佈局

我們首先需要理解一下viewBox(畫布上可以顯示的區域)和width/height的關係。在canvas中我們若改變寬高,那麼影象是可能發生扭曲的,但是svg不會,還是會以正常比例顯示。

1、當寬高與尺寸比例相同

我們先設定一個寬高和viewBox的尺寸是相同的,明顯顯示正常。

<svg viewBox="0 0 200 100" width="200" height="100">
    <rect width="100" height="50" fill="#7dc5ff"></rect>
</svg>
複製程式碼

SVG-動畫的一把好手

當我們同比例放大或縮小時,畫布上的內容也是同比例放大或縮小。

SVG-動畫的一把好手

2、當寬度比例不夠時的表現

縮小寬度,這時候發現rect這個元素變小了,其實是畫布以原始佈局居中在svg中,其餘的空間直接補充畫布區域。

<svg viewBox="0 0 200 100" width="100" height="100">
    <rect width="100" height="50" fill="#7dc5ff"></rect>
</svg>
複製程式碼

SVG-動畫的一把好手

SVG-動畫的一把好手

3、當高度比例不夠時的表現

當高度不夠時也是同理。居中,補充畫布區域。

<svg viewBox="0 0 200 100" width="200" height="50">
    <rect width="100" height="50" fill="#7dc5ff"></rect>
</svg>
複製程式碼

SVG-動畫的一把好手

這時候,比如要做一個固定在底部的寬度100%的波浪,我們只需要將width設定為100vw,而height則為同比例,即1/5的寬度即可。

<svg viewBox="0 0 1000 200"></svg>
複製程式碼
svg{
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100vw;
    height: 20vw;
}
複製程式碼

SVG-動畫的一把好手

聽說SVG動畫被CSS動畫所代替?

雖然svg元素看起來屬性非常多,且複雜,但是其實都比較好理解,且目前大部分都可以放到css中定義。

rect{
    width: 200px;
    height: 200px; 
    x: 50px;
    y: 50px;
    fill: #000;
    stroke: red;
}
path{
    d:path('M 0 0 L 100 100 Z');
}
複製程式碼

實踐中發現d:path在css中定義時,IE版本均無法支援。

既然這些屬性都可以在css中定義,那麼動畫做起來就so easy啦,transformanimation大法用起來。

咦,聽說svg自己也有做動畫的屬性呀,是的是的,但是SMIL已經被chrome棄用了(雖然說棄用了,但是用還是能用),chrome爸爸鼓勵大家用css動畫的方式來處理svg原來的動畫方案。

SVG-動畫的一把好手

當然,也不是說替代就替代的,我們一一看下,css動畫是否可以代替原來的SMIL方案。

第一種,變動一個元素的數字屬性

<svg width="300px" height="100px">
    <circle cx="0" cy="50" r="15" fill="blue" stroke="black" stroke-width="1">
        <animate attributeName="cx" from="0" to="100" dur="5s" repeatCount="indefinite" />
    </circle>
</svg>
複製程式碼

上面的animate元素的作用,翻譯過來就是改變circlecx這個屬性,從0到100,動畫時間5s,重複次數無限次。

翻譯成css就是下面的程式碼,完美無誤。

circle{
    animation: circleMove 5s infinite;
}
@keyframes circleMove{
    0%{
        cx: 0;
    }
    100%{
        cx: 100px;
    }
}
複製程式碼

第二種,變形屬性

transform也是css的拿手好戲,想怎麼變就怎麼變。

<svg width="300px" height="100px">
   <rect x="0" y="50" width="15" height="34" fill="blue" stroke="black" stroke-width="1" transform="rotation">
        <animateTransform    
            attributeName="transform"
            begin="0s"
            dur="20s"
            type="rotate"
            from="0 60 60"
            to="360 100 60"
            repeatCount="indefinite" />
   </rect>
 </svg>
複製程式碼
rect{
    animation: rectMove 20s infinite;
}
@keyframes rectMove{
    0%{
        transform: rotate(0);
    }
    100%{
        transform: rotate(360deg);
    }
}
複製程式碼

第三種,顏色屬性

css大哥表示太簡單了,不講了。

第四種,運動路徑動畫

儘管chrome46和opera33開始支援css motion path了,但是這一大片觸目驚心的紅,溜了溜了,還是先抱住SVG大哥的大腿。

SVG-動畫的一把好手
SVG-動畫的一把好手

svg的動畫大戲

上述的幾個svg動畫雖然有svg的好處,但是很多情況下純css也可以實現,但是下列這三種動畫就是svg動畫的拿手好戲了,需要這幾個的時候祭出svg妥妥的。示例

  1. 描邊動畫
  2. 改變形狀路徑動畫
  3. 沿著路徑運動

描邊動畫

svg描邊動畫主要運用了svg特有的兩個屬性,stroke-dasharraystroke-dashoffset

stroke-dasharray : 該屬性用於建立虛線:
stroke-dashoffset:指定每個實線線段的起始偏移量。正數從路徑起始點向前偏移,負數則向後。

也就是說,獲取到svg元素或者path的總長度之後,我們就可以做出一個描邊繪製的動畫。

<svg viewBox="0 0 300 300" width="300" height="300">
    <rect width="100" height="100" x="50" y="50"></rect>
</svg>
複製程式碼
rect{
  fill: transparent;
  stroke: #000;
  stroke-width: 4px;
  stroke-dasharray: 400 400; 
  stroke-dashoffset: -400px;
  animation: strokeMove 2s infinite linear;
}

@keyframes strokeMove {
  0%{
    stroke-dashoffset: -400px;
  }
  50%{
    stroke-dashoffset: 0;
  }
  100%{
    stroke-dashoffset: 400px;
  }
}
複製程式碼

stroke-dasharray: 400 400;:描邊的是一段400實線+400透明的樣式。如上,我們設定的是一個周長為400的長方形,那麼前面400的實線段剛好填充了整個長方形。
stroke-dashoffset: -400px;:實線段往前偏移-400,也就是現在用來填充長方形的剛好是後面400的透明線段。改變該值就能實現一個簡單的描邊動畫了。

SVG-動畫的一把好手

描邊動畫其實應用很廣,只要能把svg匯出比較正常的路徑,都可以通過getTotalLength()獲取到長度,然後運用上面的原理,就能實現看起來很酷炫的描邊效果了,比如描個logo、字形、科技效果都可以。

SVG-動畫的一把好手

改變形狀路徑動畫

svg形狀都可以用path來寫,例如下面的rectpath結果出來都是一個寬100高100,左上角位於(50,50)座標點的正方形。

<svg viewBox="0 0 300 300" width="300" height="300">
  <rect width="100" height="100" x="50" y="50"></rect>
  <path d="M 50 50 L 150 50 L 150 150 L 50 150 Z"></path>
</svg>
複製程式碼

既然path可以在css中定義(除IE),那麼就可以更改path來改變svg圖片的形狀,從而實現改變形狀路徑的動畫。但是要注意一點,path的點改變前後要一致,不能多了或者少了。

<svg viewBox="0 0 300 300" width="300" height="300">
  <path class="path-change"></path>
</svg>
複製程式碼
.path-change{
  fill: rgba(74,152,255,0.8);
  animation: pathChange 4s infinite;
}

@keyframes pathChange{
  0%{
    d:path('M -100 100 C 200,-50 400,300 800,100 L 800 200 L -200 200 Z');
  }

  50%{
    d:path('M -100 100 C 200,180 450,0   800,100 L 800 200 L -200 200 Z');
  }

  100%{
    d:path('M -100 100 C 200,-50 400,300 800,100 L 800 200 L -200 200 Z');
  }
}
複製程式碼

SVG-動畫的一把好手

沿著路徑運動

這裡的animateMotion定義的就是text中元素的動畫的屬性。

  • begin:動畫開始時間
  • dur:動畫持續時間
  • repeatCount:動畫重複次數
  • path:運動的路徑

這樣我們就能實現一個元素沿著特定路徑運動的動畫。

<svg viewBox="0 0 300 300" width="300" height="300">
  <text font-size="40" x="0" y="0" fill="pink"><animateMotion path="M100 50 A 20 20 0 0 1 100 200 A 20 20 0 0 1 100 50" begin="0s" dur="3s" repeatCount="indefinite"/>
  </text>
  <path d="M100 50 A 20 20 0 0 1 100 200 A 20 20 0 0 1 100 50" fill="none" stroke="#000" stroke-width="4"/>
</svg>
複製程式碼

SVG-動畫的一把好手

我們可以看到上圖的人字一直是正立的狀態,那麼如果想要各自然地有種沿著地球轉動的感覺呢,我們就需要實時改變該元素的旋轉角度,這個很容易實現,只需要加上一個屬性:

  • rotate:auto;表示自動跟隨路徑旋轉
<svg viewBox="0 0 300 300" width="300" height="300">
  <text font-size="40" x="0" y="0" fill="pink"><animateMotion path="M100 50 A 20 20 0 0 1 100 200 A 20 20 0 0 1 100 50" begin="0s" dur="3s" repeatCount="indefinite" rotate="auto"/>
  </text>
  <path d="M100 50 A 20 20 0 0 1 100 200 A 20 20 0 0 1 100 50" fill="none" stroke="#000" stroke-width="4"/>
</svg>
複製程式碼

SVG-動畫的一把好手

小結

  1. 當svg的寬高比例與原畫布比例不相同時,會保持原比例居中顯示並補充空白處;
  2. SMIL動畫雖然已經被部分瀏覽器棄用,但目前還屬於可使用狀態,但建議如簡單的屬性變化,動畫部分建議用css動畫替代,但是沿著特定路徑動畫依然需要使用animateMotion,如果是簡單的svg形狀甚至可以直接用html+css代替;
  3. 描邊動畫形狀改變動畫沿著特定路徑動畫都是目前css比較難以直接替代的一些動畫操作,這幾種型別的動畫svg都是一把好手。

參考連結

  1. 超級強大的SVG SMIL animation動畫詳解
  2. chrome棄用了SVG?
  3. CSS的motion-path屬性

相關文章