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(或改包裹 <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