preserveAspectRatio与响应式
preserveAspectRatio 回答一件事:viewBox 这块用户矩形,如何贴进 CSS viewport 盒子。它不改用户单位,也不该被当成「响应式就改内容坐标」的借口。
默认等价 xMidYMid meet:等比缩放,整幅内容可见,多余边留空白。
1. 适用 / 不适用
适用: 插画、图标、图表在不同 CSS 宽高下保持构图;封面图要裁切填满盒子(slice);刻意拉伸的数据条(none,极少)。
不适用: 无限画布相机(改 viewBox,见 viewport);用 CSS transform: scale 放大整棵 SVG DOM;每个断点重写 d / cx 去「适应宽度」。
2. meet / slice / none
| 值 | 行为 | 典型 |
|---|---|---|
meet | 等比,内容完整,可能留边 | 图标、插画、图表 |
slice | 等比,盒子填满,可能裁切 | 背景英雄图、封面 |
none | 拉伸铺满,比例破坏 | 几乎不用于插画 |
对齐用 xMin|xMid|xMax + YMin|YMid|YMax,相对的是 viewport 盒,不是页面。
xMinYMin 贴左上
xMidYMid 居中(默认)
xMaxYMax 贴右下
xMinYMid 垂直居中、水平靠左
export type ParAlign =
| "xMinYMin"
| "xMidYMin"
| "xMaxYMin"
| "xMinYMid"
| "xMidYMid"
| "xMaxYMid"
| "xMinYMax"
| "xMidYMax"
| "xMaxYMax";
export type ParMeet = "meet" | "slice";
export function setAspect(
svg: SVGSVGElement,
align: ParAlign,
meetOrSlice: ParMeet | "none",
): void {
if (meetOrSlice === "none") {
svg.setAttribute("preserveAspectRatio", "none");
return;
}
svg.setAttribute("preserveAspectRatio", `${align} ${meetOrSlice}`);
}
none 没有对齐关键字。写成 xMidYMid none 是无效的。
slice 裁掉的部分仍在用户空间里,命中和 overflow 有关。viewport 默认裁切;需要看外边的装饰时 overflow: visible,并接受溢出画进邻居布局。
3. 响应式只改 CSS 盒子
内容坐标系(viewBox="0 0 400 300" 里的 400×300)是构图。断点变化时:
- CSS 改
<svg>的width/max-width/ 容器查询。 - 保持同一 viewBox。
- 用
meet或slice决定留边还是裁切。
export function makeFluid(svg: SVGSVGElement, viewBox: string): void {
svg.setAttribute("viewBox", viewBox);
svg.removeAttribute("width");
svg.removeAttribute("height");
svg.style.width = "100%";
svg.style.height = "auto";
svg.setAttribute("preserveAspectRatio", "xMidYMid meet");
}
不要为了「窄屏显得更大」去改 viewBox 的 width/height——那是在 改相机取景,所有用户单位命中、标注坐标、连线数据一起漂。产品若真要裁一刀(只显示图的中心),那是相机 / slice,应写进明确的相机状态,而不是媒体查询里偷偷改四个数。
例外:真正的相机(白板、地图)本来就要改 viewBox,见 相机与锚点缩放。那不是响应式布局,是交互状态。
4. 线宽与缩放
盒子变大,用户单位映射到更多 CSS 像素,描边按用户单位也会变粗。要「线宽保持屏幕像素」用 vector-effect: non-scaling-stroke。其它 vector-effect 值 SVG 2 at-risk,生产不要用。
滤镜按光栅缓冲走,盒子拉大后发糊,见 滤镜合成层。
5. 失败形态
| 症状 | 原因 |
|---|---|
| 宽屏两侧空、以为坏了 | meet 的留边;要铺满改 slice 或改盒高 |
| 头像被砍掉 | slice + 对齐不是 xMidYMid |
| 图表被拉成椭圆 | none,或根本没有 viewBox |
| 断点后点击全偏 | 媒体查询改了 viewBox 内容,命中仍用旧用户坐标 |
| 缩放到很大描边像棍子 | 未 non-scaling-stroke,还把 CSS scale 当相机 |
权威资料
- SVG 2 — preserveAspectRatio
- SVG 2 — vector-effect(at-risk 除 non-scaling-stroke)
- MDN — preserveAspectRatio
核对日期:2026-08-26