本文作者:錢鴻昌(閃火)
一、我們為什麼使用svg
-
和高清png來做個對比
繼續對比
同樣高清的質地,矢量圖不畏懼放大,體積小。這裏要説明一點就是,因為 SVG 中保存的是點、線、面的信息,與分辨率和圖形大小無關,只是跟圖像的複雜程度有關,所以圖像文件所佔的存儲空間通常會比 png 小。
- 優化 SEO 和無障礙的利器,因為 SVG 圖像是使用XML(可擴展標記語言【英語:Extensible Markup Language,簡稱:XML】標記指計算機所能理解的信息符號,通過此種標記,計算機之間可以處理包含各種信息的文章等)來標記構建的,瀏覽器通過繪製每個點和線來打印它們,而不是用預定義的像素填充某些空間。這確保 SVG 圖像可以適應不同的屏幕大小和分辨率。
- 由於是在 XML 中定義的,SVG 圖像比 JPG 或 PNG 圖像更靈活,而且我們可以使用 CSS 和 JavaScript 與它們進行交互。SVG 圖像設置可以包含 CSS 和 JavaScript。在 react、vue 這種數據驅動視圖的框架下,對於 SVG 操作就更加如魚得水了。(下文會跟大家分享一些小的 SVG 動畫在我們項目中的實踐)
- 在運用層面上,SVG 提供了一些圖像編輯效果,比如屏蔽和剪裁、應用過濾器等等。並且SVG 只是文本,因此可以使用 GZip 對其進行有效壓縮。
二、瞭解 SVG 常用元素及其使用
大多數教程網上都能找到,這裏寫一些我覺得值得提及的點
2-1. svg 標籤
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="300" height="300" viewBox="0, 0, 100, 200" xmlns="http://www.w3.org/2000/svg" version="1.1">
<circle cx="100" cy="50" r="49" stroke="black"
stroke-width="2" fill="red" />
</svg>
這就是我們從設計手裏拿到的 SVG 源文件,我們掰開揉碎了説。首先我們把 SVG 內部代碼全部去掉不看,於是成了這樣
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="300" height="300" viewBox="0, 0, 100, 200" xmlns="http://www.w3.org/2000/svg" version="1.1">
</svg>
這是99% SVG 都會表現出來的形式和及一些屬性,其中包含 width、height 這兩個視口屬性,viewBox 視圖屬性,xmlns 屬性。我們一行一行看
第一行:包含了 XML 聲明,XML 聲明其實和 HTML 文檔的 DTD 聲明是類似的。類比 HTML5 的聲明方式
<!DOCTYPE html>
SVG 的文檔聲明方式(劃重點:一般如果 SVG 運用在 HTML 裏,我們可以不寫這樣的文檔聲明,但如果是單獨的 SVG 文件,那就需要寫了,否則瀏覽器可能會不認識)
<?xml version="1.0" standalone="no"?>
我們看到的 standalone 屬性是在表明該 xml 聲明是否是獨立的,如果不是即 standalone="no",那後面會引入外部的 dtd ,如第二行第三行所示。 version 屬性用於指明 SVG 文檔遵循規範的版本。 它只允許在根元素<svg> 上使用。 它純粹是一個説明,對渲染或處理沒有任何影響。雖然它接受任何數字,但是隻有1.0 和 1.1.這兩個有效的選擇。
第四行:這是 SVG 內容的開始
<svg width="300" height="300" viewBox="0, 0, 100, 200" xmlns="http://www.w3.org/2000/svg" version="1.1">
</svg>
-
xmlns 屬性是 SVG 的 XML 聲明空間,這一部分類似於 HTML 中的 xmlns="http://www.w3.org/1999/xhtml"
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -
width & height 屬性,可以理解成畫布的大小。沒錯是畫布的大小。舉個例子:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width=100 height="100"> <circle cx="50" cy="50" r="49" stroke="black" stroke-width="1" fill="red" /> </svg>
當前這個 SVG 的畫布大小是 100 * 100 的畫布,我們畫上一個半徑為 49 再加 1 個單位的描邊的圓。剛好撐滿沒毛病。所見即所得。那我們試一下改變 width 和 height。發現
<svg
style="background:#007fff"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="300"
height="300">
<circle cx="50" cy="50" r="49" stroke="black"
stroke-width="1" fill="red" />
</svg>
我們可以看到藍色區域就是我們定的 width 和 height ,圖形部分依然是那個圓沒有變化。這樣我們就理解了 width 和 height 的作用。
-
viewBox 屬性,接下來配合 viewBox 這個屬性我們再來修改下代碼
<svg style="background:#007fff" xmlns="http://www.w3.org/2000/svg" version="1.1" <!-- viewBox定義--> viewBox="0, 0, 100, 100" width="300" height="300" > <circle cx="50" cy="50" r="49" stroke="black" stroke-width="1" fill="red" /> </svg>
我們可以看到藍色區域大小不變,而我們的圓卻變得很大,大到撐滿了整個畫布。沒錯,你的想法是對的,所謂 viewBox 這個屬性可以理解為我們微信聊天時的截圖操作。viewBox 屬性的四個參數,前兩個表示截圖起點,後面兩個表示截圖終點,均是以左上角定點為原點。最後把截圖再拉伸放在 SVG 畫布上,就成了我們上面看到的 SVG 了。下面我們再修改一次 viewBox 成 0, 0, 50, 50 幫助理解
<svg
style="background:#007fff"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
viewBox="0, 0, 50, 50"
width="300"
height="300" >
<circle cx="50" cy="50" r="49" stroke="black"
stroke-width="1" fill="red" />
</svg>
所以整個邏輯大概是這樣的
2-2.path 標籤
在 SVG 裏,你可以把 path 看成是最基本的繪製元素,正因為它是最基本的,萬變不離其宗,他能演化出各種複雜的繪製效果。所以 path 是最基本也是最複雜的繪製元素。
path 的基礎屬性和其代表的意義
我們知道一個 path 標籤,最重要的屬性是 d 屬性,它是一組指令和參數的集合。在 d 屬性的值裏,我們能看到一堆非常複雜的指令字符串。
<path d="
M73.8616812,68.8664775
L74.5015359,74.5939423
L68.1746283,71.7969507
C66.2299599,72.4159872 64.1377269,72.7711218 61.9444643,72.7711218
C51.9719158,72.7711218 43.8883163,65.7823167 43.8883163,57.1611168
C43.8883163,48.5399169 51.9719158,41.5511118 61.9444643,41.5511118
C71.9164005,41.5511118 80,48.5399169 80,57.1611168
C80,61.8286883 77.6181486,66.006419 73.8616812,68.8664775" id="Fill-1" fill="#FFFFFF"></path>
其實完全不用覺得噁心,這裏繼續掰開揉碎了説
- d 屬性裏的那些指令
| 指令 | 參數 | 含義 |
|---|---|---|
| M | x y | 將畫筆移動到點(x,y) |
| L | x y | 畫筆從當前的點繪製線段到點(x,y) |
| H | x | 畫筆從當前的點繪製水平線段到點(x,y0),y0 表示繪製前畫筆所在 y 軸座標,也就是 y 軸不變 |
| V | y | 畫筆從當前的點繪製豎直線段到點(x0,y),x0 表示繪製前畫筆所在 x 軸座標,也就是 x 軸不變 |
| 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 路徑中的全部指令,其中加粗部分為常用基礎指令,相對來説比較好理解。每個指令都有對應的小寫指令。例如M 10,10 有對應的 m 10,10 。大寫代表絕對位置,所謂絕對位置即對 SVG 畫布左上角原點的絕對。小寫代表相對位置,所謂相對位置是以當前畫筆所在位置進行定位。
-
A(arc)畫弧指令
A rx ry x-axis-rotation large-arc-flag sweep-flag x y <svg width="100%" height="100%"> <path d="M0 0 A 45 45, 0, 0, 0, 45 45 L 45 0 Z" fill="green"/> </svg>
畫了張圖,幫助理解
按照圖中的步驟,我們可以畫出兩個圓都滿足,於是再看到其中A指令有三個0我們沒有解釋,回顧下 A 指令,並結合這張圖我們可以更好的理解
A rx ry x-axis-rotation large-arc-flag sweep-flag x y
- 貝塞爾曲線
關於貝塞爾曲線,張老師這篇文章已經説得非常清楚了,説得非常易懂深度理解 SVG 路徑,推薦給希望更多瞭解 svg 路徑的同學
2-3.基本圖形
基本圖形這塊相對比較好理解,我們直接一張表總結下,不做過多贅述
| 圖形 | 標籤 | 模板 | 含義 |
|---|---|---|---|
| 矩形 | < rect > | <rect x="60" y="10" rx="10" ry="10" width="30" height="30"/> |
x:起點橫座標,y:起點縱座標,rx:倒角x軸方向半徑,ry:倒角x軸方向半徑,width:寬度,height:高度 |
| 圓形 | < circle > | <circle cx="100" cy="100" r="50" fill="#fff"></circle> |
cx:圓心橫座標,cy:圓心縱座標,r:半徑 |
| 橢圓 | < ellipse > | <ellipse cx="75" cy="75" rx="20" ry="5"/> |
cx:橢圓心橫座標,cy:橢圓心縱座標,rx:橢圓x軸方向半徑,ry:橢圓y軸方向半徑 |
| 直線 | < line > | <line x1="10" x2="50" y1="110" y2="150"/> |
x1,y1:起點,x2,y2:終點 |
| 折線 | < polyline > | <polyline points="60 110, 65 120, 70 115, 75 130, 80 125, 85 140, 90 135, 95 150, 100 145"/> |
每兩個點以空格配對為一個座標點,逗號隔開形成座標集合。連成折線。 |
| 多邊形 | < polygon > | <polygon points="50 160, 55 180, 70 180, 60 190, 65 205, 50 195, 35 205, 40 190, 30 180, 45 180"/> |
類似折線,不同的是,最後一個點會自動閉合第一個點,形成閉環。 |
2-4.symbol標籤
symbol 標籤是我們直播團隊 icon 管理平台實現的核心技術點,它的作用説白話點就是相當於是一個元件,放在我們的工具箱裏,就像下面這樣:
<svg class="svg-sprite">[工具箱]
<symbol id="icon-wave_add" viewBox="0 0 76 76"><path d="M38 0a4 4 0 014 4v30h30a4 4 0 110 8H41.999L42 72a4 4 0 11-8 0l-.001-30H4a4 4 0 110-8h30V4a4 4 0 014-4z" fill="currentColor" fill-rule="evenodd" opacity="none"></path></symbol>
<symbol id="icon-time" viewBox="0 0 10 10"><path d="M5 0a5 5 0 110 10A5 5 0 015 0zm0 1.5a.5.5 0 00-.5.5v3.02l.008.088a.5.5 0 00.238.343L7.02 6.794l.082.039a.5.5 0 00.603-.215l.039-.082a.5.5 0 00-.216-.603L5.5 4.735V2l-.008-.09A.5.5 0 005 1.5z" fill="rgba(153,153,153,1)" fill-rule="evenodd" class=" "></path></symbol>
<symbol id="icon-wave_delete" viewBox="0 0 40 40"><g fill="none" fill-rule="evenodd"><circle fill="#000" opacity="0.2" cx="20" cy="20" r="20"></circle><path stroke="#FFF" stroke-width="4" stroke-linecap="round" d="M13 13l14 14M27 13L13 27"></path></g></symbol>
</svg>
放一份就可以無限引用。當它在工具箱裏時,我們是看不到它的(頁面不會渲染它),只有我們使用了<use>標籤對其進行實例引用時,我們才可以在頁面上看到它:
<use xlink:href="#icon-time"></use>
我們使用<symbol> + <use>的組合,來實現svg雪碧圖,是不是覺得很easy。
有的同學會有疑問,symbol 標籤和 g 標籤,放在 defs 裏彷彿都是在定義一個可複用的模塊,那麼兩者之間有什麼區別呢?在我的理解裏,symbol 相對於g 標籤最大的不同在於 symbol 可以給可複用代碼塊增加視圖屬性和視口屬性。方便在服用的時候直接調整到合適的運用(打印)尺寸。
三、svg 動畫及其運用
3-1.svg 動畫概要
其實關於 SVG 動畫,要説的有很多,本文我們主要説一下 SVG 動畫的一些基本屬性和運用技巧
1、SMIL 驅動
2、JavaScript 驅動
3、CSS 驅動
| 技術 | 描述 | 備註 |
|---|---|---|
| SMIL | 很強大且純粹的標籤化動畫 | 雖然 Chrome 45以後棄用了SMIL,但是依然支持,各大瀏覽器的支持度都挺好的 |
| CSS | CSS 還只能實現簡單的動畫 | offset-path 的兼容性很差。css 動畫不適合做交互性很強的動畫 |
| JavaScript | 複雜動畫就要用到 JS 了,包括世面上的一些 SVG 動畫庫,也都是 JS 去實現的 | - |
SVG 是基於 XML 的矢量圖形描述語言,可以近似理解成 HTML,所以能和 JS 以及 CSS 進行交互。 特別是 CSS,我們可以使用 CSS3 來對 SVG 做動畫處理。但是要記住的是僅當 HTML 內聯包含 SVG 文件時,我們才可以使用 CSS 對其做樣式開發。本文我們針對平時 CSS3 + HTML不容易實現,而利用 SVG 可以快速簡便實現的幾種場景做相應介紹
3-2.SVG 動畫實踐
1、直線的變化
2、path 路徑實現圖形的平滑變幻
3、描邊動畫
4、指定軌跡運動
3-2-1、直線的變化
下面這張圖是一個 GIF 的 icon,體積大約是 156KB,壓縮之後。
如果我們用 SVG 去實現的話。應該怎麼做呢。我們分為以下兩種方式,親測兼容性都 OK
CSS+SVG 實現的代碼實踐
基於 SMIL 實現的代碼實踐
總結&説明:
知識點1:
SVG 中有很多屬性我們是可以用 CSS 去描述的。在基於 CSS 動畫三劍客(animation, transform, transition)的基礎上。我們對一些屬性進行控制,就達到我們想要的動畫效果。下面兩點值得説明:
- transform:transform 有兩種用法,一個是在 SVG 標籤裏寫的 transform 屬性、另一種是在 CSS 文件裏寫的 transform,他們有着本質的區別。
<rect transform="rotate(45deg) ..." ... />
rect {
transform: rotate(45deg)
}
/** 行內的 transform 屬性,他的執行基點是在我們 svg 元素的左上角也就是 svg 的座標原點。**/
/** 而 CSS 的 transform 原點則在元素本身的中心點。**/
-
CSS可描述屬性: 很多文章告訴我們 CSS 可以控制 SVG 去做動畫,但是實際開發過程中我們會更想知道,到底哪些屬性我們可以做css控制,這裏給大家列出一些常用屬性並且可以放心使用的屬性
| CSS 可控屬性名 |可實現場景
| --- | --- |
| 理論上所有的顯示屬性,都可以使用 CSS 控制包括:比如 stroke-width、color、fill 等等 SVG 的顯示屬性|大部分的顯示樣式動態變化
|x|我們知道矩形有 x、y 屬性,其含義是起始點,控制 x,我們可以動態控制矩形的X軸位移
|y|控制 y,我們可以動態控制矩形的 Y 軸向位移
|cx|<circle cx="100" cy="100" r="50" fill="#fff" />這是一個圓形,控制 cx 可以控制圓形(或者橢圓)的 X 軸位移|
|cy|控制 cy 可以控制圓形(或者橢圓)的 Y 軸位移
|r|r 是圓的半徑,控制 r 可以控制圓形的大小
|rx|rx 是橢圓的 X 軸方向半徑,控制 rx 可以控制橢圓的大小
|ry|ry 是橢圓的 Y 軸方向半徑,控制 ry 可以控制橢圓的大小
|d|path 標籤的 d 屬性,控制 d 的路徑信息,可以控制圖形的變幻(d 屬性在 safari 上是不支持 css 描述的。我們下文會詳細的説明)|
|PS:如果各位看官們在日常開發中,不清楚該屬性是否可以通過 css 去控制,這邊給大家提供一個查詢鏈接|不支持 CSS 控制的 SVG 相關屬性
知識點2: 可以利用 SMIL 對 SVG 做動畫處理,舉個例子,同樣的動畫效果,下面這段代碼不用 CSS 也可以實現
export default function App() {
return (
<div className="App">
<svg width="100%" height="100%" viewBox="0 0 100% 100%">
{[1, 2, 3, 4, 5].map((it, index) => (
<line
key={index}
stroke="#000"
strokeWidth="2"
x1={15 + index * 5}
y1="8"
x2={15 + index * 5}
y2="22"
>
<animate
attributeName="y1"
values="8; 15; 8"
dur="1s"
begin={`${(5 % (index + 1)) * 0.2}s`}
repeatCount="indefinite"
/>
<animate
attributeName="y2"
values="22; 15; 22"
dur="1s"
begin={`${(5 % (index + 1)) * 0.2}s`}
repeatCount="indefinite"
/>
</line>
))}
</svg>
</div>
);
}
那麼
什麼是 SVG 的 SMIL 呢?
這裏不想再對其做大篇幅的贅述,因為網上有很多文章都已經説得比較詳細了[SMIL 動畫指欄](https://css-tricks.com/guide-svg-animations-smil/)、[SVG SMIL animation 動畫詳解](https://www.zhangxinxu.com/wordpress/2014/08/so-powerful-svg-smil-animation/)。 本文更想和大家交流的是在SMIL驅動和CSS驅動如何做選擇的問題。
雖然説早在 Chrome 45,chrome 就已經官宣要棄用 SMIL,但是到目前位置,各大瀏覽器廠商對它的支持度是這樣的
Chrom 宣佈棄用 SMIL 是因為要支持 CSS Animation 與 Web Animation 的發展,所以我們可以理解為當前是在一個過渡狀態,確實有一些暫時CSS 還沒法支持或者支持度很差的動畫效果,SMIL 可以輕鬆完成。但是基於 web 動畫技術發展的大趨勢,還是建議我們 SVG 動畫實現方案的選擇優先級是CSS 驅動 -> JS 驅動(我們可以採用一些框架,文末會給大家推薦一些好用的框架) -> SMIL 驅動
3-2-2、path 路徑的變化(圖形平滑變化)
CSS+SVG 實現的代碼實踐 Logo 變化
基於 SMIL 實現的代碼實踐 Logo 變化
CSS+SVG 實現的代碼實踐播放暫停
基於 SMIL 實現的代碼實踐播放暫停
總結&説明:
知識點1:
通過對<path/> d 屬性的控制,我們可以實現很多動畫效果,對於 d 屬性的控制目前有兩種方式,一種是通過 CSS 控制,另一種是通過 SMIL 控制,但是目前由於 safari 不支持用 CSS 來描述<path>標籤的 d 屬性。所以在實現這種平滑的形狀變形效果上不推薦使用 CSS。更加推薦使用SMIL或者第三方庫去實現
基於 CSS:
path {
transition: ease all 0.3s; // 就像對dom一樣的對待svg
&.play { //這裏是播放狀態下的<path />路徑
d: path("M 12,26 18.5,22 18.5,14 12,10 z M 18.5,22 25,18 25,18 18.5,14 z");
}
&.pause { //這裏是播放狀態下的<path />路徑
d: path(
"M 12,26 16.33,26 16.33,10 12,10 z M 20.66,26 25,26 25,10 20.66,10 z"
);
}
}
基於 SMIL(即通過<animate>實現對<path> d 屬性的動態控制):
const pathMap = {
from: "M 12,26 16.33,26 16.33,10 12,10 z M 20.66,26 25,26 25,10 20.66,10 z",
to: "M 12,26 18.5,22 18.5,14 12,10 z M 18.5,22 25,18 25,18 18.5,14 z"
};
<svg class="icon" viewBox="0 0 120 120" width="400" height="400">
<path
d="M 12,26 16.33,26 16.33,10 12,10 z M 20.66,26 25,26 25,10 20.66,10 z"
fill="#000000"
>
<animate
attributeName="d"
from={play ? pathMap.from : pathMap.to}
to={play ? pathMap.to : pathMap.from}
dur="0.3s"
begin="indefinite" // 這裏設置開始時間為無限以達到不自動播放的效果
fill="freeze"
/>
</path>
</svg>
以上兩個 path 路徑的切換,就可以帶來這種平滑過渡的效果。
知識點2:
我們看到的圖形變幻,都需要遵循一個原則就是點數對齊原則,什麼意思呢?我們可以看下面的demo,五角星到 10 邊形(多邊形畫的不好,抱歉...😜)。,都是 10 個控制點到 10 個控制點的過度。所以效果平滑
而下圖的 10 個點到 3 個點就沒有這種平滑的過渡效果了(當然現在很多的 SVG 動畫框架已經解決了這個問題。見文末的框架推薦 )
3-2-3、描邊動畫的應用
CSS+SVG 實現的代碼實踐-星環
CSS+SVG 實現的代碼實踐- LOGO 描邊
基於 SMIL 實現的代碼實踐-進度環
總結&説明:
知識點1:
類似的描邊動畫我們可以拿來做很多效果,比如各種形狀的進度條、比如文字的描邊、比如霓虹燈流水燈光等等流動動畫效果。而描邊動畫的核心點就在於 SVG 的兩個顯示屬性分別是 stroke-dasharray、stroke-dashoffset,我們上文説了,幾乎所有的顯示屬性都可以用 CSS 去控制,所以這種動畫,建議使用 CSS 去開發。
| 屬性 | 值舉例 | 描述 | 支持範圍 |
|---|---|---|---|
| stroke-dasharray | 1 3 4 4 | 它的值是一個序列,可以傳入多個值,分別指定描邊短線的長度和描邊線間距,多個值依次循環,如果傳入3個值,類似於 stroke-dasharray: 1,2,3。則會自動複製一份再生效 | <circle>, <ellipse>, <line>, <mesh>, <path>, <polygon>, <polyline>, <rect> <altGlyph>, <altGlyphDef>, <altGlyphItem>, <glyph>, <glyphRef>, <textPath>, <text>, <tref>, <tspan> |
| stroke-dashoffset | 10 | 描邊線段的起始位置距離圖形繪製起點的偏移量。正負值可以決定順時針還是逆時針走向 | 跟stroke-dasharray一致 |
設想一個場景,一個倒計時需要從 100 到 0,對應的視覺效果也就是從全描邊到無描邊。那麼我們初始狀態將 stroke-dasharray 的第一個值設為 2πr (周長),第二個值設也設為 2πr (周長)。那麼我們會得到一個整圓。
這時如果我們把圓展開就能看到這樣的場景
所以要實現進度的動態變化其實有兩種方案
第一種是將stroke-dasharray的第一個值從2πr(周長)調整到0。原展開圖中的黑色部分沒有了(可以理解為變成了一個點如下圖,看不見了),只剩下虛線部分是空白間隙了。
第二種是將stroke-dashoffset的值從0調整到-2πr(或者增加到2πr)。對比第一張圖成下圖的樣子
知識點2:
在實際開發中,我們會遇到一些比較複雜的圖形需要做描邊,這個時候我們沒辦法去得到它的周長是多少,這時候分兩種場景處理。一種是在CSS裏我們可以將stroke-dasharray的第二個值設置成一個非常大的數字,然後再去調整第一個值比如:
path {
stroke-dasharray: 0(調整到合適的值) 99999999999999
}
如果在js裏我們需要動態去獲取周長的話,SVG提供了原生的api可以去獲取path的周長。
const inPath = document.getElementById("inner-path");
console.log(inPath.getTotalLength());
ps:有些資料説該方法只能用於<path />,但是筆者親測了在safair和chrome上,基本可以支持所有的基礎圖形以及<path />,但是<text />不支持,瀏覽器會報not a function。
既然説到了getTotalLength(),那麼順帶説下getPointAtLength()。getPointAtLength,顧名思義就是根據距離獲取點座標。意思就是根據到起始點的距離,獲取該指定距離對應的點的座標。座標系原點為該圖形的起始點。在一些指向型的動畫上我們可能會運用到這個api。
3-2-4、軌跡運動動畫的應用
基於SMIL實現的代碼實踐-軌跡運動
總結&説明:
{/* 我們將整個飛機圖形元件用g標籤包起 */}
<g transform="translate(-100, -35) scale(0.1)">
<path
d="M164.485419 578.709313L196.274694 794.731891c0.722484 5.53904 8.188147 7.224835 11.078081 2.408278l75.860772-121.377234 740.063969-363.409219L164.485419 578.709313z"
fill="#F68206"
></path>
<path
d="M2.167451 440.233302l159.668861 132.214487 857.828787-260.334901zM289.475071 679.375353l191.217309 153.407337 542.344309-518.743179z"
fill="#FF9900"
></path>
<path
d="M204.222013 800.030103l125.23048-80.677328-48.888053-39.014111-76.342427 118.4873"
fill="#D3650B"
></path>
{/* 然後在這裏,我們利用animateMotion,去做這個軌跡運動 */}
<animateMotion
path="M 0 450 Q 150 50 250 50 Q 350 0 400 50 Q 500 50 450 200 C 300 350 250 200 500 50 C 600 50 750 200 650 250 A 50 50 0 1 1 800 50 "
begin="0s"
rotate="auto"
dur="20s"
repeatCount="indefinite"
/>
</g>
這裏我們用到了SMIL裏的<animateMotion />,animateMotion裏的path屬性,我們也可以像這樣去使用
<defs>
<path id="theMotionPath" d="xxx" />
</defs>
<animateMotion>
<mpath xlink:href="#theMotionPath"/>
</animateMotion>
實際生產中,我們這種軌跡運動的需求,是建議使用SMIL去實現的,當然CSS也是有實現方案的《使用CSS offset-path讓元素沿着不規則路徑運動》。但是CSS的兼容實在不敢恭維,勸退一波。
四、寫在最後
1、 建議將CSS動畫用於無變形的過渡或簡單動畫。尤其是在硬件加速時。CSS不需要加載其他資源(一般指三方庫),並且懸停時的小變換可以為交互帶來更好的效果。特別是當你不需要3d、物理體感、或進行大量堆疊動畫效果時建議選用CSS。另外,CSS方便調試也是很大的一個優勢。
2、對於較長的動畫,開發時會變得非常複雜且需要花精力去調試,而CSS調整時間尺度很困難,尤其是當你需要操縱一些細微幀時,個人覺得SMIL更合適做有序的,複雜的堆疊動畫羣的場景。
3、對於變形的動畫,建議使用SMIL或者第三方庫。推薦的比較優秀的三方庫有以下幾個。
| 庫名 | 描述|
| --- | --- |
| GSAP | 全稱是GreenSock Animation Platform,以前流行用 flash 的時候,GSAP就叱吒江湖的存在,GSAP有兩個版本一個是 flash 版本,一個是 javascript 版本,也就是我們説的 GSAP js。GSAP 速度快。GSAP專門優化了動畫性能,使之實現和css一樣的高性能動畫效果;輕量與模塊化;|
| Snap.svg、SVG.js、Velocity.js | 這三個庫一直會被開發者拿來對比,基本上會用jQuery,就會使用這三個庫,也就是説入手友好,Snap.svg 更偏向於支持現代瀏覽器,所以它的體量也會小一些。對比 Snap.svg 來看 SVG.js ,SVG.js 的寫法更加的清晰,使用時會有更好的體驗,且自稱提供接近完整的 SVG 規範覆蓋。Snap.svg 風格就更像一個俠客,寫起來會很瀟灑但是不好讀,Velocity 也很強大,簡單易用、高性能、功能豐富 |
| anime.js | anime.js 雖然功能沒有 GASP 強大,但是體積很樂觀,gzip壓縮完只有9kb左右,滿足日常需求開發還是足夠的 |
| D3 | Data-Driven Documents 顧名思義,更加適合用於創建數據可視化圖形場景去使用|
4、如何使用 SMIL 進行硬件加速,使用 <animateTransform>代替<animate>,並設置 x、y、z 值(z 為 0)。原理與 CSS 類似,這會將元素移到它自己的層,從而在其發生運動時不會重新繪製。
參考資料
- SVG 動畫指欄(SMIL)
- SVG 教程
- CSS 支持的 SVG 屬性查尋
- 以及文章內提及的一些文章
本文發佈自 網易雲音樂大前端團隊,文章未經授權禁止任何形式的轉載。我們常年招收前端、iOS、Android,如果你準備換工作,又恰好喜歡雲音樂,那就加入我們 grp.music-fe(at)corp.netease.com!