跳到主要内容

viewport与viewBox

<svg> 同时扮演两个角色:

  • viewport:它在 CSS 布局里占的那块盒子(width / height,或 stretched 的 replaced 元素)。
  • 用户坐标系viewBox 定义的那块抽象矩形。后续 cx="50" 的 50 是用户单位,不是 CSS 像素。

两者靠 preserveAspectRatio 对齐。细节见 preserveAspectRatio 与响应式

1. 四个数

<svg viewBox="minX minY width height" width="800" height="600">
字段含义
minX minY用户空间原点落到 viewport 的哪一点之前,先平移这段
width height用户空间里「一整幅图」的尺寸;不是 CSS 像素

viewBox="0 0 100 100" 而 CSS 是 200×200 时,用户单位 1 = 2 CSS 像素(在 meet 且比例相同的情况下)。

没有 viewBox 时,用户单位默认与 viewport 像素 1:1,缩放 CSS 盒子会把图形拉变形。生产插画 必须 有 viewBox,否则「矢量不失真」只在你碰巧没缩放时成立。

2. 不要把 width/height 当文档尺寸

export function setViewport(
svg: SVGSVGElement,
cssWidth: number,
cssHeight: number,
view: { minX: number; minY: number; width: number; height: number },
): void {
svg.style.width = `${cssWidth}px`;
svg.style.height = `${cssHeight}px`;
svg.setAttribute(
"viewBox",
`${view.minX} ${view.minY} ${view.width} ${view.height}`,
);
svg.removeAttribute("width");
svg.removeAttribute("height");
}

属性 width/height 在 inline SVG 上会参与 CSS 用作 intrinsic size。响应式场景优先 CSS 控制盒子,viewBox 控制内容。两者一起写且数字还不一样时,你很难解释「到底谁说了算」。

3. 相机就是改 viewBox

平移缩放无限画布,生产做法是改 viewBox(或改包裹 &lt;g> 的矩阵,二选一)。

interface Camera {
x: number;
y: number;
/** 用户单位 / CSS 像素;越大看得越广 */
scale: number;
}

export function cameraToViewBox(
camera: Camera,
cssWidth: number,
cssHeight: number,
): string {
const w = cssWidth * camera.scale;
const h = cssHeight * camera.scale;
return `${camera.x} ${camera.y} ${w} ${h}`;
}

不要用 CSS transform: scale() 去放大整棵 SVG DOM 冒充相机:命中、文字、线宽、滤镜光栅分辨率会一起坏。相机章见 相机与锚点缩放

4. 失败形态

症状原因
放大页面图形发糊根本没有 viewBox,SVG 被当成 stretched bitmap 盒子
图形被裁一半viewBox 原点/尺寸和内容包围盒对不上,或 overflow: hidden
改 CSS 宽高,内部坐标全乱业务把 CSS 像素当成了用户单位
宽屏左右空、窄屏裁切没设 preserveAspectRatio,或 slice/meet 选错

权威资料

核对日期:2026-08-26