新視野行銷企劃

SVG 動畫應用:從 SMIL 到 CSS scroll 動畫

SVG 動畫應用:從 SMIL 到 CSS scroll 動畫 — 扁平化風格封面圖,展示 SVG 圖標、動畫時間軸與 CSS 滾動效果的概念設計,象徵網頁動畫從傳統 SMIL 到現代 CSS 的演進過程。
回顧整個 SVG 動畫的演進歷程,我們見證了從 SMIL 到 CSS、再到 JavaScript 的技術更迭。各種方法各有其適用之處:SMIL 作為開路先鋒為 SVG 賦予了生命力,CSS 以簡潔的語法接棒覆蓋了大部分常見動畫,而 JavaScript 則憑藉無與倫比的靈活性將不可能化為可能。前端開發者唯有通曉這些工具的優劣與適用場景,才能在實際專案中揮灑自如,打造出令人驚嘆的 SVG 動態體驗。

SVG 動畫的歷史:SMIL 語法與侷限

回溯網頁向量圖形動畫的早期年代,有一種方式能在不依賴 JavaScript 或 CSS 的情況下,直接在 SVG 內賦予圖形「生命」。這項技術就是 SMIL 動畫 — 全名 Synchronized Multimedia Integration Language,原本是一種描述多媒體同步的 XML 語言,被引入 SVG 用於定義動畫效果。透過編寫 SVG 標籤中的 <animate>、<animateTransform> 等元素,開發者可以宣告式地描述圖形如何隨時間變化,例如位置移動、顏色變換或縮放旋轉。SMIL 讓靜態的 SVG 插圖猶如自帶劇本,在載入頁面後就能自動地一幕幕演出。

SMIL 的語法相當直覺:你只需在 SVG 元素內嵌入對應的動畫子元素,指定目標屬性和時間參數即可。以下是一個簡單範例,我們讓一個橘色矩形來回水平移動:

HTML/SVG
<svg width="200" height="100">
  <rect x="50" y="20" width="60" height="60" fill="orange">
    <animate attributeName="x" values="50;100;50"
             dur="4s" repeatCount="indefinite" />
  </rect>
</svg>

在這段程式碼中,<animate> 標籤定義了動畫行為:attributeName="x" 表示我們要改變矩形的 x 座標,values="50;100;50" 則設定了動畫經過的一系列數值(從 x=50 到 x=100 再回到 x=50),dur="4s" 指定動畫週期為 4 秒,repeatCount="indefinite" 則讓動畫無限循環。因此,當 SVG 載入後,我們會看到矩形以 4 秒為一個來回週期,不斷在水平軸上往返移動。

SMIL 作為 SVG 動畫的開端,展現了強大的描述力和便利性:不需撰寫任何外部程式碼,即可實現許多精美的動態效果。然而,它也存在明顯的侷限。首先,瀏覽器相容性問題曾經嚴重限制了 SMIL 的普及——並非所有主流瀏覽器都完整支援這套 SVG 動畫規範,特別是早期的 Internet Explorer 和舊版 Edge 根本不支援 SMIL,而其他瀏覽器的實作細節也不盡相同。其次,互動控制的彈性不足:由於 SMIL 採用純宣告式定義,動畫流程一旦設定就自行運行,開發者難以在執行途中動態改變效果或根據使用者行為加以控制(雖然可以透過 DOM 介面如 beginElement() 啟動或停止 SMIL 動畫,但操作範圍遠不及直接使用 JavaScript 來得靈活)。再者,規範發展停滯:SMIL 動畫在 2000 年代隨 SVG 1.1 標準問世後,後續規範更新緩慢,2008 年之後幾乎沒有新功能加入,難以跟上現代網頁開發對複雜動畫日益增長的需求。

上述因素導致 SMIL 在後來逐漸淡出主流舞台。隨著網頁技術的演進,業界開始尋找替代方案,這也為下一階段——採用 CSS 和 JavaScript 來操控 SVG 動畫——埋下了伏筆。

Q: 現在還有必要學習 SMIL 動畫嗎?
A: 這要視情況而定。目前大多數現代瀏覽器其實仍支援 SMIL(除了早已退役的 IE 和極少數行動瀏覽器),因此既有的 SMIL 動畫仍可正常運作。但由於 SMIL 技術本身已不再演進、未來支援狀況不明,主流趨勢也轉向 CSS 與 JS,一般建議將學習重點放在後兩者上。如果你已熟悉 CSS 或 JS 的動畫手法,優先使用它們即可滿足絕大部分需求。不過,瞭解 SMIL 對深入理解 SVG 生態很有幫助:它能讓你熟悉 SVG 的許多屬性與動畫概念,而且在某些 CSS 尚未覆蓋的情境下(例如沿路徑移動物件或圖形形狀漸變),SMIL 依然提供了可參考的解決思路。因此,SMIL 不見得要作為主要工具去掌握,但身為前端開發者,對其原理略有涉獵會讓你對 SVG 動畫的整體圖景有更完整的認識。

SMIL 被捨棄的原因與過渡至 CSS/JavaScript

SMIL 雖然曾經風光,但隨著時間推移,幾個關鍵因素導致它的地位被後來的技術所取代。首先是生態支援的問題:由於 SMIL 並未在所有瀏覽器上獲得完善實作,開發者在不同平臺間面臨相容性挑戰,這使大家對採用 SMIL 心存疑慮。特別是微軟的 IE/Edge 瀏覽器長期缺席 SMIL 支援,而 WebKit 引擎對 SMIL 的投入也有限。其次,瀏覽器研發團隊在 2010 年代中期開始重新審視網頁動畫的統一模型。Google Chrome 團隊一度在 2015 年公開表態計畫廢除 SMIL,轉而專注推廣更通用的動畫方案(如 CSS 動畫和 Web Animations API)。儘管隨後因社群反對而暫緩了該計畫,但這反映了業界的一種趨勢:偏好使用更通用、更具整合性的技術來實現動畫效果。透過 CSS 或 JavaScript,我們可以用一套方法同時操控 HTML 和 SVG 元素的動畫,不再局限於 SVG 特有的語法,對開發者而言無疑更加統一方便。

最後,替代方案的成熟讓 SMIL 顯得沒那麼不可或缺。CSS3 引入的動畫功能(如 @keyframes 和 transition)使我們能對 SVG 元素應用許多炫目的效果;同時 JavaScript 生態的蓬勃(含原生 Web Animations API 的誕生),也提供了以程式碼操縱動畫的強大選項。另外,一批優秀的 JS 動畫函式庫(例如 GSAP、Snap.svg 等)的崛起,進一步降低了前端實現複雜 SVG 動畫的門檻。與此同時,SMIL 的開發活躍度卻每況愈下,其大部分功能在實務中已能被上述新工具所覆蓋。綜合這些因素,業界逐漸默契地將重心從 SMIL 轉移開來,新專案更傾向使用 CSS 和 JS 來實作動畫,SMIL 則淪為歷史遺產般的存在。

需要強調的是,即便 SMIL 在趨勢上被「捨棄」,某些它擅長的獨特功能直到最近才在其他方案中找到對策。例如,沿著任意路徑移動物件這件事,純 CSS 長期以來無法輕易達成(雖然 CSS 近年加入了 Motion Path 模組,可讓元素沿自訂路徑移動,但用法較複雜且瀏覽器支援有限);又或者像漸層顏色的動畫效果、兩種複雜形狀之間的平滑過渡(morphing),這些都是 SMIL 原生支援但 CSS 一直缺席的領域。過去要實現這類特殊效果,開發者只能求助於 JavaScript 或第三方工具。因此,SMIL 的離場某種程度上也推動了瀏覽器引入更新能力來填補這些空白。我們如今處於以 CSS 和 JS 為主角的動畫時代,其實正是站在 SMIL 奠定的基礎上繼續演化。

Q: CSS 和 JS 能完全取代 SMIL 的功能嗎?
A: 大部分情況下可以。對於一般的移動、縮放、淡入淡出等效果,CSS 動畫與 JavaScript 幾乎都能實現,而且通常效能更高、控制更靈活。再配合 Web Animations API 或各種動畫庫,SMIL 能做到的事情幾乎都有替代方案。然而,仍有少數特性是 CSS 不便直接處理的。比如前述沿 SVG path 路徑移動物件、複雜圖形的形狀漸變、或 SVG 特有屬性的動畫(如漸層顏色過渡),如果不靠 JS 幫忙就很難實現。因此可以說,目前 CSS/JS 已涵蓋絕大多數動畫需求,但碰到極特殊的 SVG 動畫場景時,可能仍需一些額外的權宜之計來彌補 SMIL 所留下的空白。

經過以上轉變,SVG 動畫的主角已換成 CSS 和 JavaScript。接下來,我們先來看看 CSS 如何運用在 SVG 動畫上。

CSS 在 SVG 動畫中的應用:關鍵影格、過渡與捲動動畫

CSS 成為了現代前端控制 SVG 動態效果的有力武器。只要將 SVG 內聯到 HTML,我們便能像操控一般元素一樣,用 CSS 選擇器鎖定其中的圖形並添加動畫。典型方法包括:使用 @keyframes 定義關鍵影格動畫(描述狀態隨時間的漸變),以及利用 transition 定義狀態改變時的平滑過渡。由於許多 SVG 屬性(如填色 fill、描邊 stroke、變形 transform 等)都能被 CSS 控制,我們可以輕鬆為圖形設計各種效果,例如頁面載入時淡入、滑鼠懸停時放大等,完全不需藉助 JS 即可實現。

讓我們看一個簡單的例子:用 CSS 控制 SVG 圖形的旋轉動畫。假設有一個 SVG 正方形,希望它不停自轉,這可以用 CSS 關鍵影格動畫達成:

HTML/CSS
<svg width="100" height="100">
  <rect id="square" x="40" y="40" width="20" height="20" fill="green" />
</svg>
<style>
@keyframes spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
#square {
  transform-origin: 50% 50%;
  animation: spin 2s linear infinite;
}
</style>

上述程式碼定義了一個名為 spin 的動畫,從 0 度旋轉到 360 度。接著在選擇器 #square 上套用此動畫,並將旋轉中心 (transform-origin) 設為圖形自身的中心點(50% 50% 表示元素範圍的正中央)。如此一來,綠色方塊就會持續繞著自己的中心旋轉。整個效果完全由 CSS 達成,瀏覽器會利用最佳化的渲染管線(例如 GPU 加速)確保動畫順暢流暢。

除了使用 @keyframes,CSS 的過渡 (transition) 也經常應用在 SVG 動畫中。過渡效果的典型用法是當 SVG 元素的某個屬性發生變化時,自動插入一段平滑的改變過程。例如,可以設定一個圖標在滑鼠 hover 時改變顏色或大小,透過 CSS transition: 0.3s 來讓這些變化以 0.3 秒漸進呈現,而非瞬間跳變。相較於手動用 JS 逐步修改屬性值,CSS 過渡在簡單互動場景下更為簡潔高效。

此外,現代 CSS 也開始支援隨卷動變化的動畫。透過新的 Scroll-driven Animations 規範,我們可以將動畫時間軸直接綁定到頁面滾動位置(例如使用 @scroll-timeline),純 CSS 即可實現過去需要監聽卷軸事件才能達成的視差滾動、閱讀進度條等效果。儘管這項功能目前僅在部分瀏覽器中提供,隨時間推進將有望成為標準工具,屆時前端開發者可以更方便地打造出豐富的卷動交互動畫。

Q: CSS 可以對 <img src="image.svg"> 引入的 SVG 圖像內部進行動畫嗎?
A: 不行。當 SVG 以 <img>、CSS background-image 或 <iframe> 等方式外部載入時,它的內容並不在主頁面的 DOM 中,因此無法被外部 CSS 或 JS 選取和控制。這種情況下,SVG 更像是一張不可拆解的靜態圖片。要對 SVG 內部元素施加動畫,必須改用內聯 SVG(直接把 <svg> 代碼置於 HTML 裡),或者使用 <object>/<embed> 等方式嵌入,使 SVG 元素成為頁面 DOM 的一部分。另一種變通辦法是在 SVG 檔案內就寫好動畫(例如透過 SMIL 或該 SVG 自身的 <style> 標籤),如此即使以圖像方式嵌入,動畫也會自動播放。但總的來說,若想靈活地用 CSS 控制 SVG 的細部動畫,將 SVG 內聯進頁面是最直接有效的方式。

當然,CSS 並非萬能。若動畫需要更複雜的邏輯或精細控制,便要請出 JavaScript 登場。接下來,我們看看如何以原生 JS 深入掌控 SVG 動畫。

原生 JavaScript 實作 SVG 動畫:requestAnimationFrame、路徑長度與捲動觸發

當動畫需求超出 CSS 能力範圍時,就該由 JavaScript 粉墨登場。利用 requestAnimationFrame 這類 API,我們可以每一畫面幀動態更新 SVG 元素屬性,按照自定的邏輯打造出更多變、互動性更強的效果。例如,要在 SVG 路徑上實現手寫繪製效果,可以利用 <path> 元素的 getTotalLength() 來取得路徑總長度,接著動態調整其 stroke-dashoffset,讓線條按預定速度從起點「畫」到終點。

JavaScript 也能配合滾動事件來觸發動畫。其中使用 IntersectionObserver API 可輕鬆監測元素何時進入視窗(viewport),讓我們可以在 SVG 元素真的出現在用戶視野時才啟動動畫,未出現時則不執行,從而提升效能和用戶體驗。

以下範例展示了如何將 requestAnimationFrame 與 IntersectionObserver 結合,實現一個捲動到指定 SVG 時才啟動的路徑繪製動畫。我們有一條紅色曲線,其起始狀態被設定為隱藏(利用 stroke-dasharray 和 stroke-dashoffset),當曲線滾動進入視窗後,JavaScript 會逐步將 dashoffset 從全長減至 0,產生線條從無到有被「畫」出來的效果:

HTML/JavaScript
<svg width="100" height="100">
  <path id="path" d="M10 50 C 30 10, 70 90, 90 50"
        stroke="red" fill="none" stroke-width="3" />
</svg>
<script>
const path = document.getElementById('path');
const pathLength = path.getTotalLength();
path.style.strokeDasharray = pathLength;
path.style.strokeDashoffset = pathLength;
const duration = 2000; // 繪製持續 2 秒
let start = null;
function draw(timestamp) {
  if (!start) start = timestamp;
  const progress = Math.min((timestamp - start) / duration, 1);
  path.style.strokeDashoffset = pathLength * (1 - progress);
  if (progress < 1) {
    requestAnimationFrame(draw);
  }
}
const observer = new IntersectionObserver(entries => {
  if (entries[0].isIntersecting) {
    requestAnimationFrame(draw);
    observer.unobserve(path);
  }
});
observer.observe(path);
</script>

上述程式碼中,我們先取得 <path> 元素的總長度,並將 stroke-dasharray 和 stroke-dashoffset 設為該值,讓線條起始時完全不可見。然後定義 draw() 函式,使用 requestAnimationFrame 每幀遞減 dashoffset(根據時間計算進度 progress 從 0 到 1),逐步將紅線繪出。最後藉由 IntersectionObserver 監測路徑元素何時進入視窗,一旦出現即觸發動畫並取消監視,確保動畫只執行一次且在需要時才運行,避免資源浪費。

Q: 在 SVG 動畫中,究竟該使用 CSS 還是 JavaScript 比較好?
A: 兩者並非對立,而是各有擅長領域,亦可相輔相成。CSS 擅長描述靜態狀態間的過渡,用於簡單且可預期的效果最為便利(例如圖形的平移、縮放、變色等),宣告式語法讓動畫與樣式共存,易於維護且瀏覽器優化充分。JavaScript 則 勝在靈活:面對複雜場景、條件式互動或 CSS 無法觸及的細節(例如需依計算結果調整動畫,或操控 SVG 特有屬性),JS 能提供精細的掌控。實務上兩者經常搭配使用,例如用 JS 偵測使用者行為或特定事件,符合條件時切換 CSS 類別以觸發預定的 CSS 動畫——JS 負責「決策」,CSS 負責「演出」。這種模式融合了雙方優點:既保留了 CSS 動畫的高效順暢,又運用了 JS 提供的高度自訂能力。在 SVG 動畫開發中沒有絕對的二選一,依據需求選擇適合的手段或混合運用,才能在性能與效果之間取得最佳平衡。

總結

回顧整個 SVG 動畫的演進歷程,我們見證了從 SMIL 到 CSS、再到 JavaScript 的技術更迭。各種方法各有其適用之處:SMIL 作為開路先鋒為 SVG 賦予了生命力,CSS 以簡潔的語法接棒覆蓋了大部分常見動畫,而 JavaScript 則憑藉無與倫比的靈活性將不可能化為可能。前端開發者唯有通曉這些工具的優劣與適用場景,才能在實際專案中揮灑自如,打造出令人驚嘆的 SVG 動態體驗。

CONTACT US

網站設計報價洽詢

請填寫您的資料,我們將儘快與您聯繫! 為必填