SVG 的 viewBox 和 preserveAspectRatio 是理解 SVG 缩放的核心。下面用"容器 vs 画布"的比喻,配合实例和计算过程,帮你彻底搞懂。
一、核心概念:viewport vs viewBox
viewport(视口) = 你看到的"窗口"大小 → 由 width/height 决定
viewBox(画布) = 你画图的"坐标系" → 由 viewBox="min-x min-y w h" 决定关键比喻:
viewport = 相框(物理尺寸)
viewBox = 照片(逻辑内容)
浏览器的工作 = 把"照片"塞进"相框"里
<!-- 相框 400×200,照片 40×20 → 宽高比相同,完美适配 -->
<svg width="400" height="200" viewBox="0 0 40 20">
<rect x="10" y="5" width="20" height="10" fill="red"/>
</svg>当宽高比不一致时,问题就来了:
<!-- 相框 600×300(2:1),照片 40×30(4:3)→ 比例冲突! -->
<svg width="600" height="300" viewBox="0 0 40 30">
<rect x="10" y="5" width="20" height="15" fill="red"/>
</svg>这时候 preserveAspectRatio 就登场了。
二、preserveAspectRatio 语法
preserveAspectRatio="<alignment> <meetOrSlice>"1. alignment(对齐方式)— 9 种组合
可以拆成 X 轴对齐 + Y 轴对齐,类似 CSS 的 background-position:
| xMin(左) | xMid(中) | xMax(右) | |
|---|---|---|---|
| YMin(上) | xMinYMin | xMidYMin | xMaxYMin |
| YMid(中) | xMinYMid | xMidYMid(默认) | xMaxYMid |
| YMax(下) | xMinYMax | xMidYMax | xMaxYMax |
2. meetOrSlice(缩放规则)— 2 种
| 值 | 行为 | CSS 等价 | 说明 |
|---|---|---|---|
| meet(默认) | 等比缩放,完整显示 viewBox | background-size: contain | 可能留白,但内容不裁切 |
| slice | 等比缩放,填满 viewport | background-size: cover | 不留白,但内容可能被裁切 |
另外还有一个特殊值 none:不保持宽高比,直接拉伸填满(会变形)。
三、图解计算过程(重点!)
用这个例子来推演:
<svg width="600" height="300" viewBox="0 0 40 30">
<rect x="10" y="5" width="20" height="15" fill="red"/>
</svg>第一步:算缩放倍数
宽方向:600 ÷ 40 = 15 倍
高方向:300 ÷ 30 = 10 倍
第二步:根据 meet/slice 选倍数
meet(取小值 = 10 倍)
viewBox 放大后:40×10 = 400 宽,30×10 = 300 高
viewport 尺寸: 600 宽,300 高→ 高度刚好填满,宽度只占了 400/600,左右各有 100px 留白
┌─────────────────────────────────────────┐ ← viewport 600×300
│ 留白100 │████████████████████│ 留白100 │
│ │ viewBox 400×300 │ │
│ │ (等比放大10倍) │ │
└─────────────────────────────────────────┘slice(取大值 = 15 倍)
viewBox 放大后:40×15 = 600 宽,30×15 = 450 高
viewport 尺寸: 600 宽,300 高→ 宽度刚好填满,高度超出了 450-300 = 150px 被裁掉
┌─────────────────────────────────────────┐ ← viewport 600×300
│█████████████████████████████████████████│
│█████████████████████████████████████████│ ← viewBox 600×450
│█████████████████████████████████████████│ (等比放大15倍)
│█████████████████████████████████████████│ 下方150px被裁掉
└─────────────────────────────────────────┘第三步:根据 alignment 决定位置
以 xMinYMin slice 为例:左上角对齐,裁掉右下部分
以 xMidYMid slice 为例:居中对齐,上下各裁 75px
以 xMaxYMax slice 为例:右下角对齐,裁掉左上部分
四、实际代码对比
<!-- 1. 默认:xMidYMid meet → 居中,完整显示,左右留白 -->
<svg width="600" height="300" viewBox="0 0 40 30" style="border:1px solid blue">
<rect x="10" y="5" width="20" height="15" fill="red"/>
</svg>
<!-- 2. none → 拉伸变形,填满 -->
<svg width="600" height="300" viewBox="0 0 40 30" preserveAspectRatio="none" style="border:1px solid blue">
<rect x="10" y="5" width="20" height="15" fill="red"/>
</svg>
<!-- 3. xMinYMin slice → 左上对齐,裁掉右下 -->
<svg width="600" height="300" viewBox="0 0 40 30" preserveAspectRatio="xMinYMin slice" style="border:1px solid blue">
<rect x="10" y="5" width="20" height="15" fill="red"/>
</svg>
<!-- 4. xMidYMid slice → 居中,上下裁切 -->
<svg width="600" height="300" viewBox="0 0 40 30" preserveAspectRatio="xMidYMid slice" style="border:1px solid blue">
<rect x="10" y="5" width="20" height="15" fill="red"/>
</svg>五、常见误区 & 最佳实践
误区 1:不设 viewBox
<!-- 没有 viewBox,SVG 退化为"位图",缩放直接硬拉伸失真 -->
<svg width="400" height="200">
<circle cx="50" cy="50" r="30"/>
</svg>误区 2:width/height 与 viewBox 冲突
<!-- 显式设置 width/height 可能导致跨浏览器解析不一致 -->
<!-- 推荐:只设 viewBox,width/height 用 CSS 控制 -->
<svg viewBox="0 0 40 30" style="width:100%; height:auto">最佳实践速查
| 场景 | 推荐设置 |
|---|---|
| 图标(完整显示) | xMidYMid meet(默认即可) |
| 背景图(填满容器) | xMidYMid slice |
| 响应式 SVG | 只设 viewBox,CSS 设 width:100%; height:auto |
| 禁止变形 | 永远不要设 preserveAspectRatio="none" |
六、一句话总结
viewBox 定义"画什么",viewport 定义"在哪显示",preserveAspectRatio 定义"怎么塞进去"。
meet = contain = 完整显示,可能留白
slice = cover = 填满容器,可能裁切
none = 拉伸变形(一般不用)


