SVG 是矢量图,理论上无限缩放不失真——但实践中,“图标放大后被裁了”、“换了尺寸比例变了”、“颜色改不了”是三个最常见的坑。根源都在坐标系没搞清楚。
两个坐标系:viewport 和 viewBox
<svg width="100" height="100" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10"/>
</svg>
viewport(width="100" height="100"):SVG 元素在页面上占多少像素。类比相框尺寸。
viewBox(viewBox="0 0 24 24"):内容使用的坐标系。0 0 24 24 表示坐标从 (0,0) 到 (24,24),这块区域会被映射到 viewport。
缩放比例 = viewport ÷ viewBox:
- x 方向:100 ÷ 24 ≈ 4.17
- y 方向:100 ÷ 24 ≈ 4.17
所以内容坐标里的圆心 (12,12) 渲染在屏幕上是 (50,50)——正中间。半径 10 渲染成像素半径 41.7px。
viewBox 四个参数
viewBox="min-x min-y width height"
| 参数 | 含义 | 常见用法 |
|---|---|---|
| min-x | 视野左上角 x 坐标 | 通常 0,平移内容时改 |
| min-y | 视野左上角 y 坐标 | 通常 0 |
| width | 视野宽度(内容坐标单位) | 图标的设计稿宽度,如 24 |
| height | 视野高度 | 图标的设计稿高度,如 24 |
平移技巧:viewBox="-4 -4 32 32" 把视野扩大四圈,相当于给 24×24 的图标加了 4px 内边距——描边不再被裁剪。
preserveAspectRatio 决定宽高比不匹配时的行为
默认值 xMidYMid meet 是最常见的”安全”模式:
meet = 保持比例缩放,全部内容可见,可能有空白边
slice = 保持比例缩放,填满 viewport,可能裁边
none = 不保持比例,直接拉伸
<!-- 充满容器,裁边(适合背景装饰) -->
<svg preserveAspectRatio="xMidYMid slice" viewBox="0 0 24 24">
<!-- 拉伸(适合需要自适应宽高的图表) -->
<svg preserveAspectRatio="none" viewBox="0 0 400 300">
currentColor:图标颜色随 CSS 继承
内联 SVG 的颜色问题标准解法:
第一步:把所有硬编码颜色改成 currentColor
<!-- 改前 -->
<path fill="#333333" d="..."/>
<!-- 改后 -->
<path fill="currentColor" d="..."/>
第二步:用 CSS color 属性控制颜色
.icon {
color: var(--text-primary); /* 主题色 token */
}
.icon-danger {
color: #e53e3e;
}
原理:currentColor 是 CSS 关键字,它的值等于当前元素(或继承到的)color 属性值。SVG 属性 fill、stroke、stop-color 都支持 currentColor。
多色图标的处理:
<svg style="color: #333; --icon-accent: #4CAF50">
<path fill="currentColor" d="..."/> <!-- 主色:跟随 color -->
<circle fill="var(--icon-accent)" r="4"/> <!-- 强调色:独立变量 -->
</svg>
Figma 导出 SVG 的清理
Figma 导出的原始 SVG:
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
xmlns="http://www.w3.org/2000/svg">
<g id="icon / home" data-name="icon / home">
<path id="Vector" d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"
fill="#333333" fill-rule="evenodd"/>
</g>
</svg>
清理后:
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"
fill="currentColor" fill-rule="evenodd"/>
</svg>
去掉:id、data-name、固定 width/height、多余的 <g> 层级,fill 颜色改 currentColor。
SVG 路径 d 属性快速读法
<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/> 里的字母:
| 命令 | 含义 | 大写 | 小写 |
|---|---|---|---|
| M | Move to | 绝对坐标 | 相对坐标 |
| L | Line to | 绝对 | 相对 |
| H | 水平线 | 绝对 x | 相对 x |
| V | 垂直线 | 绝对 y | 相对 y |
| C | 三次贝塞尔 | 绝对 | 相对 |
| A | 弧线 | 绝对 | 相对 |
| Z | 闭合路径 | — | — |
不需要背,在工具里点击路径节点可视化编辑,比手改 d 字符串直观得多。
在工具里做什么
- 编辑路径节点:拖拽节点微调图标形状
- 修改颜色属性:直接改
fill/stroke,预览主题色效果 - 调整 viewBox:增加内边距,解决描边被裁的问题
- 压缩/清理:去掉 Figma 导出的冗余 id 和 data 属性,减少体积
- 复制内联代码:直接得到可粘进 HTML 的
<svg>…</svg>片段