开发者必备 SVG 手册:从入门到 Path 实战,图标、动画、自适应一次搞定 | 葡萄城技术团队


                                                                                                                                                <h1>开发者必备 SVG 手册:从入门到 Path 实战,图标、动画、自适应一次搞定</h1> 
  1. 引言

SVG 绝对是浏览器里最有意思的技术之一!用它能实现超多酷炫效果,也是前端开发工具箱里绝对关键的一环。

先给大家快速看看前端开发用 SVG 做过的一些东西: (原文此处有示例图,实际场景可替换为自己的 SVG 作品展示)

不过 SVG 也挺让人望而生畏的——这玩意儿水很深,很容易看得眼花缭乱。

所以这篇文章里,我想把最核心的基础知识讲清楚,帮大家打个扎实的底子。既能让你明白 SVG 到底酷在哪,还能教你几个马上就能用的小技巧~ ✨

适用人群

本文不需要你有任何 SVG 基础,但默认你懂点前端基础知识(HTML/CSS/JS)就行。

  1. 初识 SVG

SVG(”可缩放矢量图形”,Scalable Vector Graphics)本质上是一种图像格式,就像 .jpg.gif 一样。我们可以像用其他图片那样,把它塞进 <img> 标签里:

<img 
  alt="返回首页" 
  src="https://my.oschina.net/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>

img

很多 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 里画直线就很简单:指定起点和终点,直线就出来了!

img

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 规范里把这种情况叫”退化图形”(听着有点狠)——像矩形这种二维图形,一旦只剩一个维度,就会被判定为”无效”,不渲染。

以前不同浏览器表现还不一致,有的会渲染”退化图形”,有的不会。好在现在所有现代浏览器都遵循规范了。

img

圆角矩形

还能用 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,圆形也会消失。

img

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>

img

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 坐标,浏览器会把这些点连起来,最后还会把”最后一个点”和”第一个点”连起来,形成闭合图形。

我刚开始学的时候还懵过:我以为”多边形”特指三角形、正方形、六边形这种”对称图形”(规范里叫”正多边形”),但其实正多边形只是多边形的一种。

img

画正多边形(需要 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 外部尺寸是 300×300,viewBox="0 0 300 300":内部坐标和像素 1:1,图形正常显示。
  • 如果 viewBox="0 0 150 150":视野只看内部 150×150 的区域,相当于把图形放大 2 倍(因为外部还是 300×300)。

这就像浏览器的缩放功能(Ctrl +):窗口大小没变,但内容放大了。

为什么要用 viewBox

实际开发里,我们一般把 viewBox 设为固定值——这样不管 SVG 外部尺寸怎么变,显示的内容始终一致。比如同一个图标,在按钮上用 24×24,在导航栏用 48×48,只要 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 的理解会完全不一样~

                                                                                </div>



Source link

未经允许不得转载:紫竹林-程序员中文网 » 开发者必备 SVG 手册:从入门到 Path 实战,图标、动画、自适应一次搞定 | 葡萄城技术团队

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
关于我们 免责申明 意见反馈 隐私政策
程序员中文网:公益在线网站,帮助学习者快速成长!
关注微信 技术交流
推荐文章
每天精选资源文章推送
推荐文章
随时随地碎片化学习
推荐文章
发现有趣的