动态

详情 返回 返回

開發者必備 SVG 手冊:從入門到 Path 實戰,圖標、動畫、自適應一次搞定 - 动态 详情

開發者必備 SVG 手冊:從入門到 Path 實戰,圖標、動畫、自適應一次搞定

  1. 引言

SVG 絕對是瀏覽器裏最有意思的技術之一!用它能實現超多酷炫效果,也是前端開發工具箱裏絕對關鍵的一環。

先給大家快速看看前端開發用 SVG 做過的一些東西: (原文此處有示例圖,實際場景可替換為自己的 SVG 作品展示)

不過 SVG 也挺讓人望而生畏的------這玩意兒水很深,很容易看得眼花繚亂。

所以這篇文章裏,我想把最核心的基礎知識講清楚,幫大家打個紮實的底子。既能讓你明白 SVG 到底酷在哪,還能教你幾個馬上就能用的小技巧~ ✨

適用人羣

本文不需要你有任何 SVG 基礎,但默認你懂點前端基礎知識(HTML/CSS/JS)就行。

  1. 初識 SVG

SVG("可縮放矢量圖形",Scalable Vector Graphics)本質上是一種圖像格式,就像 .jpg.gif 一樣。我們可以像用其他圖片那樣,把它塞進 <img> 標籤裏:

<img 
  alt="返回首頁" 
  src="/images/home.svg" 
/>

這方法能跑通,但根本沒體現出 SVG 的厲害之處。真正的"魔法",要從內聯 SVG 説起。

.jpg 這類圖片格式都是二進制的------你用文本編輯器打開,看到的全是亂碼。但 SVG 不一樣,它用 XML 語法定義,跟 HTML 特別像!它不是給每個像素指定 RGB 顏色,而是包含了"繪製圖像所需的一系列指令"。

更神奇的是,我們能直接把 SVG 源碼丟進 HTML 文檔裏:

代碼示例

<!-- index.html -->
<div class="wrapper">
  <p>快看這個 SVG:</p>
  <svg width="100" height="100">
    <!-- 圓形:填充色 hotpink,半徑 30,圓心在 (50,50) -->
    <circle 
      fill="hotpink" 
      r="30" 
      cx="50" 
      cy="50" 
    />
  </svg>
</div>

<style>/* styles.css */
.wrapper {
  padding: 20px;
  font-family: "Microsoft YaHei", sans-serif;
}
</style>

img

HTML 給的"基礎組件"都是跟文檔相關的------段落、標題、列表,跟 Word 裏的功能差不多。SVG 其實是一個道理,只不過它的"基礎組件"全是用來畫圖的,比如 <circle>(圓形)、<polygon>(多邊形)、<path>(路徑)這些。

最酷的是:SVG 是 DOM 裏的"一等公民"!我們能用 CSS 和 JS 選擇、修改 SVG 節點,就像操作 HTML 元素一樣。

再看這個例子,鼠標 hover 時圓形會變大、下移:

<!-- index.html -->
<style>/* styles.css */
circle {
  fill: hotpink;
  /* 過渡動畫:半徑 400ms,y 座標 600ms */
  transition: r 400ms, cy 600ms;
}
/* 鼠標懸浮/聚焦時,半徑變大到 50,y 座標移到 100 */
button:hover circle,
button:focus-visible circle {
  r: 50px;
  cy: 100px;
}
button {
  border: none;
  background: transparent;
  cursor: pointer;
}
</style>

<button>
  <svg width="100" height="100">
    <circle r="30" cx="50" cy="50" />
  </svg>
</button>

很多 SVG 屬性(比如圓形的填充色 fill、半徑 r)其實也能當 CSS 屬性用。這意味着我們能用 CSS 改它們,甚至加過渡動畫!🤯

這才是 SVG 的威力所在------它就像一個"平行世界的 HTML",專注於畫圖而非文檔,而且我們能用已有的 CSS/JS 技能讓它動起來。

手寫 SVG

可能有點反直覺,但我經常在代碼編輯器裏寫 SVG,而不是用 Illustrator 或 Figma 這類設計工具。

設計工具確實能導出 SVG,但它往往會把所有內容揉進一個 <path> 標籤裏。這樣一來,想給單個元素做動畫就難多了------而在我看來,這正是 SVG 最酷的地方。

當然也看場景:如果圖形複雜度太高,用專業工具還是更實際。但像我之前展示的那些效果,手寫代碼反而更簡單。

  1. 基本圖形

就像剛才看到的,SVG 有自己的"繪圖基礎組件"。不是 <div><button>,而是 <circle><polygon> 這類圖形。咱們一個個説。

3.1 直線(Lines)

最簡單的圖形大概就是 <line> 了:

<svg width="280" height="280">
  <!-- 直線:起點 (80,80),終點 (200,200),描邊色 oklch(0.9 0.3 164),描邊寬 5 -->
  <line 
    x1="80" 
    y1="80" 
    x2="200" 
    y2="200" 
    stroke="oklch(0.9 0.3 164)" 
    stroke-width="5" 
  />
</svg>

屬性説明:

  • x1/y1:直線的起點座標
  • x2/y2:直線的終點座標

這事兒看着簡單,但在 HTML 裏還真不好實現------HTML 裏畫對角線,得弄個細長的 DOM 元素再旋轉,要是想精確控制起點終點,還得算數學題。

但 SVG 裏畫直線就很簡單:指定起點和終點,直線就出來了!

3.2 矩形(Rectangles)

矩形用 <rect> 標籤,通過左上角座標 x/y 定位,再用 width/height 控制大小:

<svg width="300" height="300">
  <rect 
    x="60"      <!-- 左上角 x 座標 -->
    y="100"     <!-- 左上角 y 座標 -->
    width="180" <!-- 寬度 -->
    height="100"<!-- 高度 -->
    fill="none" <!-- 不填充顏色 -->
    stroke="oklch(0.9 0.3 164)" <!-- 描邊色 -->
    stroke-width="5" <!-- 描邊寬 -->
  />
</svg>
跟 HTML <div> 的區別

乍一看像帶邊框的 <div>,但有兩個核心區別:

  1. SVG 描邊是畫在"路徑正中間"的,不是內側或外側------而且這沒法配置(所有圖形都這樣)。
  2. 如果把 widthheight 設為 0,矩形會直接消失!SVG 規範裏把這種情況叫"退化圖形"(聽着有點狠)------像矩形這種二維圖形,一旦只剩一個維度,就會被判定為"無效",不渲染。

以前不同瀏覽器表現還不一致,有的會渲染"退化圖形",有的不會。好在現在所有現代瀏覽器都遵循規範了。

net%2F%2F6200f335d4fe2bfbe2cf717347facfa6.jpg&pos_id=img-q1nsi9Sl-1758594281314)

圓角矩形

還能用 rx/ry 給矩形加圓角,類似 CSS 的 border-radius

<svg width="340" height="340">
  <rect 
    x="80" 
    y="100" 
    width="500" 
    height="500" 
    rx="100"  <!-- 水平圓角半徑 -->
    ry="50"   <!-- 垂直圓角半徑 -->
    stroke="green" 
    stroke-width="5" 
    fill="none" 
  />
</svg>

3.3 圓形(Circles)

圓形用 <circle> 標籤,大小由半徑 r 控制,位置由圓心座標 cx/cy 控制:

<svg width="280" height="280">
  <circle 
    cx="140"  <!-- 圓心 x 座標 -->
    cy="140"  <!-- 圓心 y 座標 -->
    r="70"    <!-- 半徑 -->
    fill="none" 
    stroke="oklch(0.9 0.3 164)" 
    stroke-width="5" 
  />
</svg>

跟矩形一樣:如果把 r 設為 0,圓形也會消失。

3.4 橢圓(Ellipses)

橢圓 <ellipse> 其實就是"可拉伸的圓形"------水平半徑和垂直半徑可以設不同值,用來畫橢圓:

<svg width="300" height="300">
  <ellipse 
    cx="150"  <!-- 圓心 x 座標 -->
    cy="150"  <!-- 圓心 y 座標 -->
    rx="75"   <!-- 水平半徑 -->
    ry="50"   <!-- 垂直半徑 -->
    fill="none" 
    stroke="oklch(0.9 0.3 164)" 
    stroke-width="5" 
  />
</svg>

3.5 多邊形(Polygons)

<polygon> 能畫多角形,比如五邊形、星形這些:

<svg width="300" height="300">
  <polygon 
    points="
      60,100   <!-- 點 1 -->
      100,180  <!-- 點 2 -->
      140,140  <!-- 點 3 -->
      180,180  <!-- 點 4 -->
      220,100  <!-- 點 5 -->
    " 
    fill="none"
    stroke="oklch(0.9 0.3 164)"
    stroke-width="3"
  />
</svg>
關鍵屬性 points

points 裏填一系列 X/Y 座標,瀏覽器會把這些點連起來,最後還會把"最後一個點"和"第一個點"連起來,形成閉合圖形。

我剛開始學的時候還懵過:我以為"多邊形"特指三角形、正方形、六邊形這種"對稱圖形"(規範裏叫"正多邊形"),但其實正多邊形只是多邊形的一種。

畫正多邊形(需要 JS)

想畫正多邊形得用三角函數,這裏給個可直接運行的示例(國內 Playground 可用,需引入 Lodash):

<!-- index.html -->
<svg class="parent-svg" width="400" height="400">
  <polygon class="mister-polygon" fill="none" stroke="green" stroke-width="3" />
</svg>

<!-- 引入 Lodash(國內 CDN) -->
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
<script src="./index.js"></script>
// index.js
const svg = document.querySelector('.parent-svg');
const polygon = document.querySelector('.mister-polygon');

// 可修改:邊數、半徑
const numOfSides = 8; // 8 邊形
const radius = 80;    // 外接圓半徑

function drawPolygon() {
  const svgWidth = Number(svg.getAttribute('width'));
  const svgHeight = Number(svg.getAttribute('height'));
  const cx = svgWidth / 2; // SVG 中心 x 座標
  const cy = svgHeight / 2; // SVG 中心 y 座標

  // 生成每個頂點的座標
  const points = _.range(numOfSides).map((index) => {
    // 旋轉偏移:讓偶數邊圖形(如六邊形、八邊形)"邊朝上",而非"角朝上"
    const rotationOffset = numOfSides % 2 === 0 ? Math.PI / numOfSides : 0;

    // 計算當前頂點的角度
    const angle = (index * 2 * Math.PI) / numOfSides - Math.PI / 2 + rotationOffset;

    // 極座標轉直角座標
    const x = cx + radius * Math.cos(angle);
    const y = cy + radius * Math.sin(angle);
    return `${x},${y}`;
  });

  // 設置多邊形的頂點
  polygon.setAttribute('points', points.join(' '));
}

drawPolygon();

3.6 路徑(Paths)------SVG 的"萬能畫筆"

前面聊的 circle、rect 都是"基礎款"圖形,但如果想畫更復雜的東西------比如自定義圖標、logo、甚至手寫字體,就得靠 SVG 裏的"萬能選手":<path> 元素了。

<path> 是 SVG 中最靈活、也最強大的圖形元素,沒有之一。它不像其他形狀有固定的"輪廓",而是靠一串"繪圖指令"控制------就像給畫筆寫了個"行動腳本",讓它按步驟畫出你想要的任何形狀。

3.6.1 先搞懂:<path> 怎麼工作?

<path> 的核心是 d 屬性("d" 代表 "data" 或 "drawing"),所有繪圖指令都寫在 d 裏。這些指令由"字母+數字"組成,比如 M10 20 L30 40,就像在告訴畫筆:"先挪到 (10,20),再畫直線到 (30,40)"。

有個關鍵點要記:指令字母分大小寫------

  • 大寫字母(如 MLC):用「絕對座標」(相對於 SVG 畫布的原點 (0,0));
  • 小寫字母(如 mlc):用「相對座標」(相對於當前畫筆的位置)。

剛開始建議先用大寫字母,不容易搞混,熟悉後再用小寫簡化代碼。

3.6.2 常用繪圖指令:從簡單到複雜

咱們按"難度梯度"拆解常用指令,每個指令配小示例,直接複製到 CodeSandbox 就能跑~

(1)基礎指令:移動與直線(畫折線、多邊形)

先掌握最基礎的 3 個指令,就能畫各種直線組成的圖形:

| 指令 | 含義 | 語法示例 | 説明 |
| ---- | ------------------ | -------------------------- | ---------------------------------------- |
| M | 移動畫筆(不畫線) | M x yM x1 y1 x2 y2 | 把畫筆挪到指定位置,準備開始畫 |
| L | 畫直線 | L x yL x1 y1 x2 y2 | 從當前位置畫直線到指定位置 |
| Z | 閉合路徑(收尾) | Z(無參數) | 從當前位置畫直線迴路徑起點,形成閉合圖形 |

示例:用 M/L/Z 畫一個三角形
<svg width="300" height="300" viewBox="0 0 300 300">
  <!-- path 畫三角形:移動到(150,50) → 直線到(250,200) → 直線到(50,200) → 閉合 -->
  <path 
    d="M 150 50 L 250 200 L 50 200 Z" 
    fill="none" 
    stroke="hotpink" 
    stroke-width="3" 
  />
</svg>

效果:一個粉色描邊的三角形,Z 指令自動把最後一個點(50,200)連回起點(150,50),不用手動寫 L 150 50

(2)簡化直線指令:H(水平直線)和 V(垂直直線)

如果要畫水平或垂直直線,不用寫 L,用 H(Horizontal)和 V(Vertical)更方便:

| 指令 | 含義 | 語法示例 | 説明 |
| ---- | ---------- | -------- | --------------------------------------- |
| H | 畫水平直線 | H x | 從當前位置畫水平直線到 x 座標(y 不變) |
| V | 畫垂直直線 | V y | 從當前位置畫垂直直線到 y 座標(x 不變) |

示例:用 H/V 畫一個矩形(替代 <rect>
<svg width="300" height="300" viewBox="0 0 300 300">
  <!-- 移動到(50,50) → 水平右移到250 → 垂直下移到200 → 水平左移到50 → 閉合 -->
  <path 
    d="M 50 50 H 250 V 200 H 50 Z" 
    fill="none" 
    stroke="oklch(0.9 0.3 164)" 
    stroke-width="3" 
  />
</svg>

效果和 <rect x="50" y="50" width="200" height="150"> 一樣,但 path 更靈活------比如想在矩形右邊加個小凸起,直接加指令就行。

(3)進階指令:曲線(畫平滑圖形)

基礎指令只能畫直線,想畫圓、橢圓、波浪線、愛心這些帶弧度的圖形,就得用曲線指令。最常用的是「貝塞爾曲線」指令,咱們重點講兩個:C(三次貝塞爾曲線)和 Q(二次貝塞爾曲線)。

① 二次貝塞爾曲線 Q:簡單曲線(一個控制點)

二次貝塞爾曲線需要 1 個"控制點"和 1 個"終點",控制點決定曲線的"彎曲方向"和"程度",就像用手指按住繩子中間掰彎它。

| 指令 | 含義 | 語法示例 | 説明 |
| ---- | -------------- | ------------- | ---------------------------------- |
| Q | 二次貝塞爾曲線 | Q cx cy x y | cx,cy = 控制點座標;x,y = 終點座標 |

示例:用 Q 畫一個笑臉的嘴巴
<svg width="200" height="200" viewBox="0 0 200 200">
  <!-- 笑臉的眼睛(兩個小圓) -->
  <circle cx="70" cy="80" r="10" fill="black" />
  <circle cx="130" cy="80" r="10" fill="black" />
  
  <!-- 嘴巴(二次貝塞爾曲線):起點(60,120) → 控制點(100,150) → 終點(140,120) -->
  <path 
    d="M 60 120 Q 100 150 140 120" 
    fill="none" 
    stroke="black" 
    stroke-width="5" 
  />
</svg>

試着改控制點 100 150100 100,會發現嘴巴從"微笑"變成"撇嘴"------控制點越遠,曲線越彎曲。

img

② 三次貝塞爾曲線 C:複雜曲線(兩個控制點)

三次貝塞爾曲線需要 2 個"控制點"和 1 個"終點",能畫出更復雜的弧度,比如 S 形、波浪線,常用於 logo 或自定義圖形。

| 指令 | 含義 | 語法示例 | 説明 |
| ---- | -------------- | ----------------------- | ---------------------------------------------------------- |
| C | 三次貝塞爾曲線 | C cx1 cy1 cx2 cy2 x y | cx1,cy1 = 第一個控制點;cx2,cy2 = 第二個控制點;x,y = 終點 |

示例:用 C 畫一個愛心

愛心是經典的 path 案例,用兩個三次貝塞爾曲線就能實現:

<svg width="200" height="200" viewBox="0 0 200 200">
  <path 
    d="M 100 40 C 140 0 180 60 100 120 C 20 60 60 0 100 40 Z" 
    fill="hotpink" 
    stroke="none" 
  />
  <!-- 拆解指令:
  1. M 100 40 → 起點(愛心頂部)
  2. C 140 0 180 60 100 120 → 右半顆心(兩個控制點:(140,0) 拉右上,(180,60) 拉右下,終點(100,120)是底部)
  3. C 20 60 60 0 100 40 → 左半顆心(控制點:(20,60) 拉左下,(60,0) 拉左上,終點回到起點)
  4. Z → 閉合路徑(其實這裏C的終點已經是起點,Z可省略,但加上更規範)
  -->
</svg>

複製到 CodeSandbox 就能看到粉色愛心,改控制點的座標(比如把 140 0 改成 150 10),愛心的形狀會跟着變,大家可以多試幾次感受規律。

img

(4)實用技巧:簡化曲線指令(ST

如果連續畫多條貝塞爾曲線,比如畫波浪線,每次都寫全 CQ 會很麻煩。SVG 提供了 S(三次貝塞爾曲線簡化版)和 T(二次貝塞爾曲線簡化版),能自動繼承上一個控制點的"對稱點",少寫一半參數。

比如用 S 畫連續的 S 形曲線:

<svg width="300" height="150" viewBox="0 0 300 150">
  <!-- 第一條C曲線:M 20 75 C 50 25 100 125 150 75 
       第二條S曲線:S 200 25 280 75(自動繼承上一個控制點(100,125)的對稱點(200,25)) -->
  <path 
    d="M 20 75 C 50 25 100 125 150 75 S 200 25 280 75" 
    fill="none" 
    stroke="oklch(0.9 0.3 164)" 
    stroke-width="3" 
  />
</svg>

效果是一條平滑的 S 形曲線,S 指令只需要寫"第二個控制點"和"終點",比連續寫 C 簡潔多了。

3.6.3 <path> 的實戰:動畫與優化

<path> 不僅能畫複雜圖形,還能結合之前講的"動畫描邊",實現更酷的效果。另外,咱們也聊聊 path 代碼的優化技巧------畢竟手寫 path 容易亂,工具導出的 path 又可能冗餘。

(1)實戰:讓 <path> 自繪製(愛心動畫)

延續之前"SVG 自繪製"的思路,給愛心 path 加個"逐漸畫出來"的動畫,核心還是用 stroke-dasharraystroke-dashoffsetgetTotalLength()

<!-- index.html -->
<svg width="200" height="200" viewBox="0 0 200 200">
  <path 
    id="heart-path"
    d="M 100 40 C 140 0 180 60 100 120 C 20 60 60 0 100 40 Z" 
    fill="none" 
    stroke="hotpink" 
    stroke-width="3" 
  />
</svg>

<script>
// 1. 獲取path元素
const heartPath = document.getElementById('heart-path');
// 2. 計算path的總長度(關鍵:getTotalLength() 對path同樣有效)
const pathLength = heartPath.getTotalLength();

// 3. 初始設置:讓虛線"藏起來"
heartPath.style.strokeDasharray = `${pathLength}, 10000`; // 實線=總長,空白=很大
heartPath.style.strokeDashoffset = pathLength; // 偏移=總長,實線偏移到看不見

// 4. 頁面加載後觸發動畫:實線逐漸顯示
window.addEventListener('load', () => {
  heartPath.style.transition = 'stroke-dashoffset 2000ms ease-in-out';
  heartPath.style.strokeDashoffset = 0; // 偏移歸0,畫出愛心
});
</script>

複製到 CodeSandbox 運行,會看到粉色的愛心"一筆畫"出來,和之前多邊形的自繪製邏輯完全一致------這就是 SVG 動畫的通用性!

(2)優化技巧:讓 <path> 代碼更易讀

不管是手寫還是工具導出的 pathd 屬性裏的數字堆在一起都像"亂碼"。其實我們可以給 d 加格式,就像之前優化 polygonpoints 一樣:

<!-- 優化前:一堆數字擠在一起,難讀 -->
<path d="M100 40 C140 0 180 60 100 120 C20 60 60 0 100 40 Z" />

<!-- 優化後:按指令換行,加註釋,一目瞭然 -->
<path d="
  M 100 40          <!-- 起點:愛心頂部 -->
  C 140 0 180 60    <!-- 右半心:控制點1(140,0),控制點2(180,60) -->
    100 120         <!-- 右半心終點:愛心底部 -->
  C 20 60 60 0      <!-- 左半心:控制點1(20,60),控制點2(60,0) -->
    100 40          <!-- 左半心終點:回到起點 -->
  Z                 <!-- 閉合路徑 -->
" />

SVG 會忽略 d 裏的空格和換行,所以放心加格式------隊友和未來的你會感謝這份清晰!

(3)工具導出 path 的注意事項

如果用 Figma/Illustrator 畫複雜圖形(比如 logo),導出 SVG 時會自動生成 path,但可能有冗餘代碼(比如多餘的節點、重複指令),建議做兩步優化:

  1. 簡化路徑 :Figma 裏選圖形 → 右鍵 → "簡化路徑"(Reduce Points),減少節點數量,讓 d 更短;
  2. 清理代碼 :用在線工具(比如 SVGOMG:https://svgomg.net/,國內可訪問)去除冗餘屬性(如 fill-opacity="1" 這種默認值),讓代碼更乾淨。
3.6.4 <path> 的使用場景總結

現在你應該明白為什麼 <path> 是 SVG 的"萬能畫筆"了吧?它的核心優勢是"無所不能",適合這些場景:

  • 自定義圖標(比如 App 裏的特殊按鈕、導航圖標);
  • 複雜圖形(比如 logo、插畫、手寫字體);
  • 動態生成的圖形(比如數據可視化裏的折線圖、雷達圖,用 JS 動態拼接 d 指令)。

雖然 path 的指令看起來多,但常用的也就 M/L/Z/Q/C 這幾個,多畫幾個小例子(比如箭頭、星星、咖啡杯),很快就能上手~

SVG 代碼格式化

不管是 <polygon>points 還是 <path>d,多餘的空格和換行在 SVG 規範裏都是"可選的"。以前這麼寫是為了優化文件大小,但現在服務器都用 gzip 壓縮了,多幾個符號對體積影響微乎其微。

所以強烈建議大家給 SVG 加格式!用户看不出差別,但同事(還有未來的你)會感謝你寫的"可讀 SVG"。

  1. 可縮放 SVG

之前咱們用的都是"絕對座標"------SVG 必須是固定大小,否則就會出問題。比如這個例子:

<!-- 固定 300x220 的 SVG,圓形在中心 -->
<svg width="300" height="220">
  <circle 
    cx="150" 
    cy="110" 
    r="60" 
    stroke="#FFD700" 
    stroke-width="10" 
    fill="none"
  />
</svg>

如果把 SVG 寬度改小,圓形不會縮小,反而會被截斷------這跟普通圖片不一樣(jpg 會跟着容器縮放)。

笨辦法:用 JS 動態計算

有一種解法是"根據容器寬度動態改所有屬性"------比如默認寬度 300px,要是容器只有 150px,就把 cxr 這些值都乘以 0.5。但這太麻煩了,哪怕一個簡單圖形都要寫一堆計算邏輯。

好辦法:用 viewBox 屬性

這才是 SVG 縮放的精髓!給 SVG 加個 viewBox,就能定義"內部座標系"------圖形不再用 DOM 的像素值,而是用這個內部座標系:

<!-- 容器寬度可改,但圖形會自動縮放 -->
<svg 
  width="300"  <!-- 外部容器寬度(可改) -->
  viewBox="0 0 300 220"  <!-- 內部座標系:x0,y0 到 x300,y220 -->
>
  <circle 
    cx="150"  <!-- 內部座標:中心在 (150,110) -->
    cy="110" 
    r="60" 
    stroke="#FFD700" 
    stroke-width="10" 
    fill="none"
  />
</svg>
viewBox 怎麼理解?

viewBox 接受 4 個值,可拆成兩組來看:

  1. 前兩個值(x, y):控制"視野起點"------相當於在 SVG 的"無限畫布"上移動視野,看不同區域。
  2. 後兩個值(width, height):控制"視野大小"------相當於"縮放級別",不改變 SVG 外部尺寸,只改變內部圖形的顯示比例。

舉個例子:

  • 如果 SVG 外部尺寸是 300x300,viewBox="0 0 300 300":內部座標和像素 1:1,圖形正常顯示。
  • 如果 viewBox="0 0 150 150":視野只看內部 150x150 的區域,相當於把圖形放大 2 倍(因為外部還是 300x300)。

這就像瀏覽器的縮放功能(Ctrl +):窗口大小沒變,但內容放大了。

為什麼要用 viewBox

實際開發裏,我們一般把 viewBox 設為固定值------這樣不管 SVG 外部尺寸怎麼變,顯示的內容始終一致。比如同一個圖標,在按鈕上用 24x24,在導航欄用 48x48,只要 viewBox 相同,圖形就不會變形。

而且矢量圖的優勢就是"無限縮放不失真"------位圖(jpg/png)放大後會出像素塊,但 SVG 是數學指令,放大 10 倍、100 倍都依然清晰!

  1. 表現屬性

SVG 圖形可以用 fill 填色,用 stroke 畫邊框,也可以兩者都用。fill 很好理解,咱們重點説 stroke------它有點像 HTML 的 border,但功能強多了。

先看個示例,切換不同選項看看 stroke 能玩出什麼花樣:

<svg viewBox="0 0 200 200">
  <circle 
    cx="100" 
    cy="100" 
    r="50" 
    fill="none"
    stroke="hsl(45deg 100% 50%)"  <!-- 描邊色:金色 -->
    stroke-width="6px"            <!-- 描邊寬 -->
    stroke-dasharray="20, 14"     <!-- 虛線:20px 實線 + 14px 空白 -->
    stroke-linecap="butt"         <!-- 端點樣式:無(默認) -->
  />
</svg>
常用 stroke 屬性説明

| 屬性名 | 作用 |
| ------------------ | ------------------------------------------------------------ |
| stroke | 描邊顏色,默認透明(transparent) |
| stroke-width | 描邊寬度,單位像素 |
| stroke-dasharray | 虛線樣式:傳多個值,分別代表"實線長度"和"空白長度",循環重複。比如 10,20 是"10px 實線+20px 空白",5,3,2 是"5px+3px空白+2px+3px空白" |
| stroke-linecap | 描邊端點樣式:butt(無,默認)、round(圓形端點)、square(方形端點) |

這些屬性既可以寫在 SVG 標籤裏( inline 屬性),也可以用 CSS 控制------比如把 stroke-width="5" 改成 style="stroke-width: 5px;"

  1. 動畫描邊

既然 stroke 相關屬性能當 CSS 屬性用,那肯定能加動畫!

比如給描邊加過渡效果,鼠標 hover 時變色、變寬:

circle {
  stroke: hsl(45deg 100% 50%);
  stroke-width: 6px;
  /* 過渡動畫:所有 stroke 屬性都加動畫 */
  transition: stroke 1200ms, stroke-width 900ms, stroke-dasharray 1500ms;
}
circle:hover {
  stroke: hsl(164deg 100% 40%); /* hover 時變綠色 */
  stroke-width: 8px;
  stroke-dasharray: 10, 5;       /* hover 時虛線更密集 */
}

6.1 用 stroke-dashoffset 做動畫

stroke-dashoffset 是個超有用的屬性------它能讓虛線"偏移滑動"。比如讓虛線繞着圖形跑:

<svg viewBox="0 0 200 200">
  <rect 
    x="50" 
    y="50" 
    width="100" 
    height="100" 
    fill="none"
    stroke="oklch(0.9 0.25 164)"
    stroke-width="5"
    stroke-dasharray="10, 10"  <!-- 10px 實線 + 10px 空白 -->
    stroke-dashoffset="0"      <!-- 初始偏移 0 -->
    style="animation: casinoLights 400ms linear infinite;"
  />
</svg>

<style>
/* 動畫:讓虛線偏移滑動 */
@keyframes casinoLights {
  from { stroke-dashoffset: 0; }
  to { stroke-dashoffset: 20; } /* 偏移量 = 實線+空白(10+10),無縫循環 */
}
</style>
關鍵技巧:無縫循環

要讓動畫不"跳幀",stroke-dashoffset 的目標值要等於"實線長度 + 空白長度"(比如上面的 10+10=20)。

6.2 模擬"SVG 自繪製"效果

這是最經典的 SVG 動畫之一------讓圖形像"被畫出來"一樣逐漸顯示。原理很簡單:

  1. stroke-dasharray 的"實線長度"等於圖形的"路徑總長","空白長度"設得很大(比如 10000)。
  2. 初始時讓 stroke-dashoffset 等於"路徑總長"(虛線偏移到看不見)。
  3. 動畫時把 stroke-dashoffset 減到 0,實線逐漸顯示出來。

示例代碼(以多邊形為例,path 動畫見 3.6.3 節):

<svg viewBox="0 0 280 320">
  <!-- 這裏用三角形舉例,實際可替換成任意圖形 -->
  <polygon 
    id="triangle"
    points="140,20 260,220 20,220" 
    fill="none"
    stroke="oklch(0.9 0.3 164)"
    stroke-width="3"
  />
</svg>

<script>
// 1. 獲取圖形元素
const triangle = document.getElementById('triangle');
// 2. 計算圖形的路徑總長(關鍵方法:getTotalLength())
const pathLength = triangle.getTotalLength();
// 3. 設置虛線:實線長度 = 路徑總長,空白長度 = 10000(足夠大)
triangle.style.strokeDasharray = `${pathLength}, 10000`;
// 4. 初始偏移 = 路徑總長(虛線看不見)
triangle.style.strokeDashoffset = pathLength;

// 5. 觸發動畫(比如頁面加載後)
setTimeout(() => {
  triangle.style.transition = 'stroke-dashoffset 3000ms';
  triangle.style.strokeDashoffset = 0; // 偏移到 0,圖形逐漸顯示
}, 500);
</script>
簡化方案:用 pathLength 屬性

如果不想用 JS 計算路徑總長,可以給 SVG 標籤加 pathLength 屬性,手動定義"路徑總長"(比如設為 100):

<svg viewBox="0 0 280 320">
  <polygon 
    points="140,20 260,220 20,220" 
    fill="none"
    stroke="oklch(0.9 0.3 164)"
    stroke-width="3"
    pathLength="100"  <!-- 手動定義路徑總長為 100 -->
    style="
      stroke-dasharray: 100, 10000; 
      stroke-dashoffset: 100; 
      transition: stroke-dashoffset 3000ms;
    "
  />
</svg>

<script>
// 直接改偏移量即可,不用計算真實長度
setTimeout(() => {
  document.querySelector('polygon').style.strokeDashoffset = 0;
}, 500);
</script>

這個方法更簡單,尤其適合動態生成的圖形------但本質是"讓瀏覽器假裝路徑總長是 100",實際長度沒變,只是計算簡化了。

  1. SVG 的強大之處

這篇文章幫大家把 SVG 的核心基礎講透了:從簡單的 line、rect,到萬能的 path 元素,再到可縮放、表現屬性和動畫描邊,足夠支撐你做很多實際需求。但 SVG 的玩法遠不止這些------比如濾鏡(Filter)、蒙版(Mask)、漸變(Gradient),還有結合 JS 做數據可視化(比如用 D3.js 生成動態圖表),這些都是更進階的方向。

我目前正在做一門關於"趣味動畫"的綜合課程,SVG 是課程的核心內容。我做前端快 20 年了,在動畫上踩過很多坑,也總結了不少技巧,希望能在課程裏把這些"秘籍"都分享給大家!

這門課計劃幾個月後開啓"搶先體驗",想了解更新的話,可以在這裏訂閲(原文此處有訂閲鏈接,國內可替換為公眾號/課程平台鏈接):

最後:動手試試!

SVG 最忌諱"只看不動手"------建議你現在就打開 CodeSandbox,試着用今天學的知識畫一個小作品:比如用 path 畫個咖啡杯,加個 viewBox 讓它自適應,再給杯柄加個描邊動畫。哪怕圖形很簡單,親手實現後你對 SVG 的理解會完全不一樣~

user avatar toopoo 头像 ting_61d6d9790dee8 头像 u_15641375 头像 zhulongxu 头像 weishiledanhe 头像 yeshan333 头像 bencjl 头像 blueberrypie 头像 lishisan 头像 liuyuedekele 头像 huanledeyanjing 头像 aran_tu 头像
点赞 17 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.