跳到主要内容

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)是构图。断点变化时:

  1. CSS 改 <svg>width / max-width / 容器查询。
  2. 保持同一 viewBox。
  3. meetslice 决定留边还是裁切。
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 当相机

权威资料

核对日期:2026-08-26