10 分鐘 svg 動畫 (1) — SMIL

xxlee (Ching Hung Lee)
Practicode
Published in
6 min readJul 5, 2017

--

本篇預設讀者對 svg 元素有基本的認識,若無基礎者建議先將將此部分補上

本篇預計產出之結果:

那麼讓我們開始吧!

先簡介這篇的重點,四個常用到的 svg 動畫元素:

<set>
<animate>
<animateMotion>
<animateTransform>

它們各自的功用為:

set: 將一個元素的attribute改為指定的值

<!-- 在兩秒後將半徑從原本的200變為250 -->
<circle cx="300" cy="300" r="200" fill="#000">
<set attributeName="r" to="250" begin="2s">
</circle>

animate: 和set一樣是將元素的attribute改為指定的值,但是多了中間的過場動畫

<!-- 在兩秒後將半徑從原本的200,經過一秒的時間變漸漸變為250 -->
<circle cx="300" cy="300" r="200" fill="#000">
<animate attributeName="r" to="250" begin="2s" dur="1s">
</circle>

animateMotion: 將元素依照指定的路徑做移動

<!-- 讓此矩型在兩秒內沿著path移動(此例中的路徑為圓形) -->
<rect width="20" height="20" x="0" y="0" fill="#000">
<animateMotion
dur="2s"
path="M150,300a150,150 0 1,0 300,0a150,150 0 1,0 -300,0"/>
</rect>

animateTransform: 改變元素的 transform 特性(svg 與 css 相同,可以使用 translate、scale、rotate、skew、matrix),並有中間的過場動畫

<!-- 讓此矩型在兩秒內以(75, 75)為圓心旋轉一圈 -->
<rect width="50" height="50" x="50" y="50" fill="#000">
<animateTransform dur="2s"
attributeName="transform"
type="rotate"
from="0, 75, 75"
to="360, 75, 75" />
</rect>

如果還是沒有很了解的話沒關係,接下來會邊做邊解釋其中的細節!

1.首先,我們先將最外圈不會動的橘色軌道建好

<svg width="600" height="600">
<circle cx="300" cy="300" r="210" fill="orange"></circle>
<circle cx="300" cy="300" r="200" fill="white"></circle>
</svg>

在這邊不用 stroke 的方式建立圓環,而是用一白一橘的實心圓疊合的原因是,目前的 svg 的 stroke 只能向內長(可以參考這篇 stack overflow),而向內長會造成後面新增滾動的黑球無法完美的貼合在圓環上。

2.接下來,將黑球放置在軌道最左邊(較容易計算座標位置)

<circle cx="150" cy="300" r="50" fill="#000"></circle>

3.新增放大(scale)的動畫

<animate id="scale"
attributeName="r"
to="50"
dur=".5s"
fill="freeze" />

最後一個attribute fill="freeze"代表動畫結束後會停在最後的狀態,預設為 remove。這邊也可以使用 animateTransform 的 scale 來完成,不過可能會出下下圖的情況:座標跟著一起被放大了,需要另外再把他 translate 回來,有興趣的人可以自行嘗試看看。

4.最後要做的就是重頭戲:一直轉一直轉!我們先到這個網站將黑球的圓形軌跡轉為路徑,圓心和軌道相同,半徑則為白色大圓的半徑減掉黑球的半徑。接下來利用 <animateMotion> 將得到的路徑填進 path 這個屬性。 additive="sum" 代表將所有的動畫效果疊加在一起,否則後者的動效將會蓋過前者。

<circle cx="150" cy="300" r="50" fill="#000">
<animate id="scale"
attributeName="r"
to="50"
dur=".5s"
fill="freeze" />
<set begin="scale.end" attributeName="cx" to="0"></set>
<set begin="scale.end" attributeName="cy" to="0"></set>
<animateMotion begin="scale.end"
dur="2s"
path="M150,300a150,150 0 1,0 300,0a150,150 0 1,0 -300,0"
additive="sum"
repeatCount="indefinite"
rotate="auto" />
</circle>

這邊要注意的是一旦使用<animateMotion> ,則該元素座標的原點會以路徑的起點為準(這裡是 M150,300 ),所以在 scale 動畫結束的同時( begin="scale.end"),使用了 <set> 元素將黑球圓心的座標改為 (0, 0)。 repeateCount="indefinite" 代表不斷重播這個動畫, rotate="auto" 代表黑球會隨著運動路徑做旋轉,若將黑球改為矩形則可明顯看出效果。

附上完整程式碼的 codepen:

svg 真的充滿許多小眉角,不親自用一次的話不會知道,但靈活運用可以達成許多 css 無法完成的事,尤其是路徑 animateMotion 這部份,是十分值得投資的一塊領域!

--

--