十分鐘、五步驟,SVG 動起來!

“When you look at someone through rose-colored glasses, all the red flags just look like flags.”

前言

在 Codepen 上常常看到很多會設計又懂寫前端的高手,用 SVG 畫出很漂亮的圖案後,還能讓他們產生可愛的動畫,我一直很好奇他們是怎麼實作的,總覺得很困難,好像得對 SVG 透徹了解,並且自己畫出那些 SVG 圖案,才有辦法實作動畫。
但其實不然,今天就來分享一個簡單的小技巧,讓你在短短十分鐘內就能讓一個靜態的 SVG 圖案活躍起來!

簡單看個成品,你可以點選下面範例中的 build 按鈕,可以看到原本靜止的 SVG 房子動了起來!


See the Pen
SVG Animation House
by Arvin (@arvin0731)
on CodePen.

第一步,先找到你喜歡的 SVG 圖案

如果你跟我一樣,畫不出漂亮的圖案或 Icon,可以到 FlaticonIconfindericons8 等網站找些免費的 svg icon 下載使用(注意授權即可)。

Find a good icon

前陣子到美國出差發現他們好像很流行滑板車(scooter),那就到 Flaticon 上找個類似的圖案來玩玩!(一開始的房屋範例也是從 Flaticon 取得的):



Icons made by Freepik from www.flaticon.com

第二步,分析一下你的 SVG 長什麼樣子

找到喜歡的 svg icon 後,用瀏覽器打開,接著開啟 devtool,你會看到下面的結果:

open svg from browser

裡面的 pathcircle 都是 svg 的 DOM 元件,跟字面上意思一樣,就是 svg 圖案內的線條與圓形。

svg 的 path,基本上就是一連串的 svg mini language:

1
2
3
4
<path d="M 10 25
L 10 75
L 60 75
L 10 25">

上面程式碼中的 d 的內容:M 代表將 筆 移動到 (10, 25),接著 L 畫一條線到 (10, 75),以此類推。詳細介紹可看這裡 - SVG Paths and D3.js

透過 devtool,我們可以知道每個 path 是對應到畫面上的哪個部分:

devtool-find-parts

到這邊大概就會有個方向了,既然我們可以知道每個元件對應到圖案上的哪個部分,我們就能夠針對想要套上動畫的 DOM 元件來操作!

第三步,好用的動畫工具 - TimelineLite & TweenMax

當然你可以給予每個 svg 內的 DOM 元件一個獨特的 id,或是 class name,然後用 CSS 或 JavaScript 來自行處理動畫,但這樣難度還是頗高,更重要的是,就沒辦法在十分鐘內做完 XD

所以我們得借用工具,Timeline(Lite|Max) 跟 TweenMax 是知名的 GreenSock Animation Platform(簡稱 GSAP)推出的套件,從名稱就可以猜出,主要是提供 時間軸補間 動畫的 API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

<!-- index.html -->
<div class="example">
<div class="example__ball"></div>
<h1 class="example__title">Taiwan No.1!</h1>

<button onclick="animateCircle()"> Build! </button>
</div>
<!-- JavaScript -->
<script>
// 使用 TimelineMax 物件
const tl = new TimelineMax();
// 利用 .set 設定擁有 class name .example__title 的元素
// 將其 scale 縮小成 0.2,透明度設為 0
tl
.set(".example__title", {
scale: 0.2,
autoAlpha: 0
})
// 將擁有 class name .example__ball 的元素
// scale 縮小成 0.2
.set(".example__ball", {
scale: 0.2
})
// tween 1: 在 1 秒內旋轉 360 度,並翻轉 180 度
// 接著回復 scale 成原始大小,中間以 Elastic.easeIn 這個 ease function 來控制動畫速度
.to(".example__ball", 1, {
rotationX: 360,
rotationY: 180,
scale: 1,
ease: Elastic.easeIn.config(2, 1)
})
// tween 2: 跟第一個補間動畫雷同,只是這次是控制文字,並在 0.5 內完成
.to(".example__title", 0.5, {
autoAlpha: 1,
scale: 1,
ease: Back.easeOut.config(4)
});
</script>

GSAP 甚至提供一個 Ease Visualizer 讓你可以看看每種 Ease function 的效果,更順帶附上程式碼:

gsap ease visualizer

上述短短的程式碼就能達到如下效果:


See the Pen
GSAP Tutorial: Simple Timeline
by Arvin (@arvin0731)
on CodePen.

第四步,結合 GSAP 工具與你喜愛的 SVG

GSAP 的 API 文件蠻齊全的,還有論壇可以搜尋相關問題,使用上有任何問題幾乎都能在上面找到答案:官網文件論壇

在一開始的房屋範例中,我主要使用的是 TimelineMax 的 fromstaggerFrom,跟前步驟內的範例不同,這兩個 API 只需要設定初始值,他會在指定時間內將補間動畫完成:

1
tl.from('#House > rect:nth-child(24)', 1, {scaleX: 0, transformOrigin: "center", ease: Power2.easeOut})

將 CSS Selector #House > rect:nth-child(24) 這個元素,從 scaleX 為 0 開始,以 center(中心) 為變形起點,利用 Power2.easeOut 的 ease function,在一秒內回復到原始狀態,並執行補間動畫。

1
.staggerFrom(['#House > path:nth-child(34)', '#House > path:nth-child(32)'], 0.8, {scaleY: 0, transformOrigin: "bottom", ease: Bounce.easeOut, stagger:0.2}, 0, "scene1+=0.5")

from 雷同,只是 staggerFrom 可以一次放入多個 CSS Selector,用 stagger 這個屬性來設定陣列中的 Selector 要以怎樣的時間差出現。

詳細 API 參數可以參考官方文件

接著回到我們的 SVG,在 devtool 的幫助下,要取出 svg 內部元素的 CSS Selector 非常容易,在 DOM 元件上按右鍵,選擇 Copy -> Copy selector,就能直接複製到該元件的 CSS Selector:

copy selector directly

現在我們能取得 svg 中任何部分的 CSS Selector,也知道我們能用 GSAP API 來進行補間動畫,現在是時候將其結合起來!

第一步先調整一下基本 Layout,一般在空白 Html 內直接放入 svg 時,圖案大多會緊靠頁面左上角,因為 svg 本身通常不會有任何關於 layout 的 CSS,所以可以套用個 margin: 0 auto 將其置中,看起來會順眼一些,你也能額外加些 padding。此外,為了方便測試動畫效果,也可以放個 button 在頁面上,用來呼叫你的動畫函式:

1
2
3
4
5
6
7
8
9
10
11
<!--html part-->
<button onclick="animateBike()"> Build! </button>
<!--css part-->
<style>
#Capa_1 {
margin: 0 auto;
display: block;
width: 256px;
height: 100%;
}
</style>

接著我們使用 TimelineMax 提供的 staggerFrom 函式,利用 devtool 將滑板車的輪子部分找出來,複製它們的 CSS Selector,放入 staggerFrom 函式參數中,設定 x 與 y 軸的 scale 都從 0 開始,由 center 增長,採用 Bounce.easeOut 的 ease function,而四個 Selector 間以 stagger: 0.2 的屬性值作為補間動畫出現的時間差:

1
2
3
4
const tl = new TimelineMax();
tl
.staggerFrom(['#Capa_1 > g > path:nth-child(1)', '#Capa_1 > circle:nth-child(7)', '#Capa_1 > path:nth-child(6)', '#Capa_1 > circle:nth-child(5)'],
1, {scaleY:0, scaleX: 0, transformOrigin: "center", ease: Bounce.easeOut, stagger:0.2})

簡單幾行程式碼,就能讓我們的滑板車有了動畫!


See the Pen
Scooter - demo1
by Arvin (@arvin0731)
on CodePen.

第五步,發揮創意,將整體動畫完成

TimelineMax 所提供的 API 都是 chainable 的,你可以想像成是時間軸一般,動畫跟著 chain 一步一步照順序執行,而 staggerFrom 則可以同時讓多個 DOM 元件以些微時間差的順序啟動,另外也能設置一些 Flag 來指定要等到哪幾個動畫完成後,才接續其他動畫,這些細節當你在思考要如何“動畫”你的 SVG 時,再去 GSAP 的文件與論壇查詢,相信都能找到解答。

最後,發揮自己的創意,把 svg 的各個部分都補上動畫,搭配 GSAP 提供的文件,組合各種 API,一個簡單的作品就完成了:


See the Pen
Scooter - final-steps
by Arvin (@arvin0731)
on CodePen.

結論

雖然這個小技巧說破真的不值錢,最困難的技術都交給了 GSAP 處理,SVG 還不用是自己畫的,但加在自己的部落格或是放在投影片中,可以瞬間讓畫面更加豐富,無聊沒事拿來娛樂自己也是很不錯的選擇,像是出國旅行回來有時差睡不著,不如就來畫個動畫調養身心 XD
總之,我自己覺得蠻有趣的,希望或多或少對讀到這篇文章的人有點幫助。

資料來源

  1. GreenSock Animation Platform
  2. How to Create Beautiful SVG Animations Easily