第 8 章:坐标系、变换和单位

8.1. 介绍

所有 SVG 内容都绘制在 SVG 视口 内。每个 SVG 视口定义了一个绘图区域,该区域具有尺寸(宽度、高度)和原点,以抽象的 用户单位 为度量。

请注意,SVG 视口一词不同于 CSS 中使用的 “视口” 术语。

初始视口是一个顶层 SVG 视口,它在包含环境使用的坐标系(例如网页浏览器中的 CSS 像素)与 用户单位 之间建立映射。关于建立初始视口的更详细说明,请参见 初始视口

SVG 视口只能由元素建立。有关哪些元素会生成视口的信息,请参见 建立新的 SVG 视口

每个 SVG 视口都会生成一个 视口坐标系 和一个 用户坐标系,最初它们是相同的。在视口元素上提供 viewBox 会按照《viewBox属性》所述,将用户坐标系相对于视口坐标系进行变换。视口的子元素可以进一步修改 用户坐标系,例如通过指定 transform 属性。

SVG 视口可以嵌套。百分比单位的解析以最近的祖先 SVG 视口的宽度和高度为参考。因此,嵌套 SVG 视口提供了一种重新定义百分比单位含义并为相对特定矩形区域“适配”图形提供新参考矩形的机会。

SVG 视口的宽度、高度以及原点由生成该视口的 SVG 文档片段与该片段的父级(无论是真实的还是隐式的)之间的协商过程决定。有关此协商过程的描述,请参阅 建立新的 SVG 视口

默认情况下,嵌套 SVG 视口的 视口坐标系 等同于父元素的本地坐标系,并在 SVG 视口元素的原点处进行平移。然而,在 SVG 视口元素上设置的 transform 属性会相对于父元素的用户坐标系修改 视口坐标系

抽象地说,所有 SVG 视口都嵌入在 画布 中——一个在所有相关维度上都是无限大的绘图区域。

8.2. 计算 SVG 视口的等价变换

此过程将 viewBox 属性的 min-xmin-ywidthheight 值、定义该属性的元素的位置与尺寸,以及该元素上的 preserveAspectRatio 属性值,转换为平移和缩放,从而作用于该元素所包含的内容。

  1. vb-xvb-yvb-widthvb-height 分别为 viewBox 属性的 min-xmin-ywidthheight 值。
  2. e-xe-ye-widthe-height 分别为该元素的位置和尺寸。
  3. alignpreserveAspectRatio 的对齐值;如果未定义 preserveAspectRatio,则取默认值 'xMidYMid'
  4. meetOrSlicepreserveAspectRatiomeetOrSlice 值;如果未定义 preserveAspectRatio,或该值中缺少 meetOrSlice,则取默认值 'meet'
  5. scale-x 初始化为 e-width/vb-width
  6. scale-y 初始化为 e-height/vb-height
  7. 如果 align 不等于 'none'meetOrSlice'meet',则把较大的 scale-xscale-y 中的较小者设为相等。
  8. 否则,如果 align 不等于 'none'meetOrSlice'slice',则把较小的 scale-xscale-y 中的较大者设为相等。
  9. translate-x 初始化为 e-x - (vb-x * scale-x)。
  10. translate-y 初始化为 e-y - (vb-y * scale-y)。
  11. 如果 align 包含 'xMid',则向 translate-x 加上 (e-width - vb-width * scale-x) / 2。
  12. 如果 align 包含 'xMax',则向 translate-x 加上 (e-width - vb-width * scale-x)。
  13. 如果 align 包含 'yMid',则向 translate-y 加上 (e-height - vb-height * scale-y) / 2。
  14. 如果 align 包含 'yMax',则向 translate-y 加上 (e-height - vb-height * scale-y)。

对该元素所包含的内容应用的变换为 translate(translate-x, translate-y) scale(scale-x, scale-y)。

8.3. 初始视口

初始视口的宽度必须是 width 表示属性在 最外层 svg 元素 上的取值,除非满足以下条件:

在上述条件下,视口的宽度必须通过这些定位属性来确定。

同样,如果在引用元素或 最外层 svg 元素 上指定了足以确定视口高度的 定位属性,则这些定位属性必须决定视口的高度;否则,初始视口的高度必须是 height 表示属性在 最外层 svg 元素 上的取值。

如果 widthheight 表示属性在 最外层 svg 元素 上使用了 用户单位(即未提供单位标识符),则该值被视为等同于相同数值的 “px” 单位(参见 Units)。

在下面的示例中,SVG 图形以内联方式嵌入在使用 CSS 布局规则的父 XML 文档中。由于未在 最外层 svg 元素 上提供 CSS 定位属性,width="100px"height="200px" 属性决定了初始视口的大小。

<?xml version="1.0" standalone="yes"?>
<parent xmlns="http://some.url">

   <!-- SVG graphic -->
   <svg xmlns='https://w3org.cn/2000/svg'
      width="100px" height="200px">
      <path d="M100,100 Q200,400,300,100"/>
      <!-- rest of SVG graphic would go here -->
   </svg>

</parent>

8.4. 初始坐标系

对于 最外层 svg 元素,SVG 用户代理必须确定一个初始的 视口坐标系 与一个初始的 用户坐标系,使两者相同。两个坐标系的原点都必须位于 SVG 视口的原点,并且初始坐标系中的一个单位必须等同于 SVG 视口中的一个 CSS 2.1 px([CSS2] 第 4.3.2 节)。在独立 SVG 文档以及嵌入(通过引用或内联)在其父文档且父文档布局由 CSS 决定的 SVG 文档片段中,初始视口坐标系(因此也包括初始用户坐标系)必须把原点设在视口的左上角,正 x 轴指向右方,正 y 轴指向下方,文本以“正立”方向渲染,即罗马字母和亚洲文字的全尺寸表意字符的顶部边缘向上、右侧边缘向右。

如果 SVG 实现是支持使用 CSS 2.1 兼容的 px 单位进行文档样式化的用户代理的一部分,则 SVG 用户代理应将其 px 单位在现实世界中的初始大小设为与其他样式操作使用的值相同;否则,如果用户代理能够从其环境中确定 px 单位的大小,则应使用该值;否则,它应自行选择一个合适的 px 单位大小。无论何种情况,px 的大小必须符合 CSS 2.1 中的规定([CSS2] 第 4.3.2 节)。

下面的 Example InitialCoords 显示了初始坐标系统的原点位于左上角,x 轴指向右侧,y 轴指向下方。初始用户坐标系统的一个用户单位等于父(隐式或显式)用户代理的“像素”。

<?xml version="1.0" standalone="no"?>
<svg width="300px" height="100px" version="1.1"
     xmlns="https://w3org.cn/2000/svg">
  <desc>Example InitialCoords - SVG's initial coordinate system</desc>

  <g fill="none" stroke="black" stroke-width="3" >
    <line x1="0" y1="1.5" x2="300" y2="1.5" />
    <line x1="1.5" y1="0" x2="1.5" y2="100" />
  </g>
  <g fill="red" stroke="none" >
    <rect x="0" y="0" width="3" height="3" />
    <rect x="297" y="0" width="3" height="3" />
    <rect x="0" y="97" width="3" height="3" />
  </g>
  <g font-size="14" font-family="Verdana" >
    <text x="10" y="20">(0,0)</text>
    <text x="240" y="20">(300,0)</text>
    <text x="10" y="90">(0,100)</text>
  </g>
</svg>
Example InitialCoords — SVG's initial coordinate system

Example InitialCoords

以 SVG 格式查看此示例(仅限启用 SVG 的浏览器)

8.5. ‘transform’ 属性

用户代理必须支持 transform 属性及其表现属性,具体定义见 [css-transforms-1]。

8.6. ‘viewBox’ 属性

名称 初始值 可动画
viewBox [<min-x>,? <min-y>,? <width>,? <height>] 如同未指定一样。
<min-x>, <min-y>, <width>, <height> = <number>

svg 元素上使用的 transform 有些特殊,因为它受 viewBox 属性的影响。该变换应当被视为如果 svg 拥有一个父元素且该父元素设置了同样的变换时的效果。

决议:transform 属性在概念上作用于 ‘svg’ 元素的外部,表现属性与样式属性在视觉结果上没有区别

viewBox 属性配合 preserveAspectRatio 属性,可实现将 SVG 视口拉伸以适配特定容器元素的能力。

viewBox 属性的值是由四个数字 <min-x><min-y><width><height> 组成的列表,数字之间以空白或逗号分隔,用来指定应映射到由该元素建立的 SVG 视口边界的用户空间矩形,同时考虑 preserveAspectRatio 属性的影响。存在 viewBox 时,会如《计算 SVG 视口的等价变换》所述,对视口坐标系进行一次变换。

<width><height> 为负值,则为错误并使 viewBox 属性失效。若值为零,则禁用该元素的渲染。

ViewBox 示例 展示了在 最外层 svg 元素 上使用 viewBox 属性,以指定 SVG 内容应拉伸以适配 SVG 视口边界的用法。

<?xml version="1.0" standalone="no"?>
<svg width="300px" height="200px"
     viewBox="0 0 1500 1000" preserveAspectRatio="none"
     xmlns="https://w3org.cn/2000/svg">
  <desc>Example ViewBox - uses the viewBox
   attribute to automatically create an initial user coordinate
   system which causes the graphic to scale to fit into the
   SVG viewport no matter what size the SVG viewport is.</desc>
  <!-- This rectangle goes from (0,0) to (1500,1000) in user coordinate system.
       Because of the viewBox attribute above,
       the rectangle will end up filling the entire area
       reserved for the SVG content. -->
  <rect x="0" y="0" width="1500" height="1000"
        fill="yellow" stroke="blue" stroke-width="12"  />
  <!-- A large, red triangle -->
  <path fill="red"  d="M 750,100 L 250,900 L 1250,900 z"/>
  <!-- A text string that spans most of the SVG viewport -->
  <text x="100" y="600" font-size="200" font-family="Verdana" >
    Stretch to fit
  </text>
</svg>
Example ViewBox
渲染为
SVG 视口示例
width=300px,
height=200px
      渲染为
SVG 视口示例
width=150px,
height=200px
Example ViewBox - stretch to fit 300 by 200       Example ViewBox - stretch to fit 150 by 200

以 SVG 格式查看此示例(仅限启用 SVG 的浏览器)
 

使用 viewBox 属性的效果是,用户代理会自动提供适当的变换矩阵,将用户坐标系中指定的矩形映射到指定区域(通常是 SVG 视口)的边界。若要实现左图所示的效果,且 SVG 视口尺寸为 300 × 200 像素,用户代理需要自动插入一个将 X 与 Y 同时缩放至 0.2 的变换。该效果等同于拥有一个 300 px × 200 px 的 SVG 视口,并在文档中加入如下的补充变换:

<?xml version="1.0" standalone="no"?>
<svg width="300px" height="200px"
     xmlns="https://w3org.cn/2000/svg">
  <g transform="scale(0.2)">
    <!-- Rest of document goes here -->
  </g>
</svg>

要实现右图所示的效果,且 SVG 视口尺寸为 150 × 200 像素,用户代理需要自动插入一个将 X 缩放至 0.1、Y 缩放至 0.2 的变换。该效果等同于拥有一个 150 px × 200 px 的 SVG 视口,并在文档中加入如下的补充变换:

<?xml version="1.0" standalone="no"?>
<svg width="150px" height="200px"
     xmlns="https://w3org.cn/2000/svg">
  <g transform="scale(0.1 0.2)">
    <!-- Rest of document goes here -->
  </g>
</svg>

注意,在某些情况下,用户代理除了需要提供 scale 变换外,还必须提供 translate 变换。例如,在 最外层 svg 元素 上,如果 viewBox 属性为 <min-x><min-y> 指定了非零值,则必须加入相应的 translate

如果同时对同一元素应用了 transform(或 patternTransform)以及 viewBox,则会建立两个新的坐标系。transform 为该元素建立第一个新坐标系;viewBox 为该元素的所有后代建立第二个坐标系。第一个坐标系在后面乘以第二个坐标系。

transform 属性不同,由 viewBox 自动创建的变换不会影响该元素的 ‘x’‘y’‘width’‘height’ 属性(在 marker 元素的情况下,还包括 markerWidthmarkerHeight)。因此,在上例中展示的带有 widthheight 表示属性和 viewBoxsvg 元素中,widthheight 表示的是 viewBox 变换**之前**的坐标系中的数值。另一方面,和 transform 属性一样,它会为所有其他属性以及后代元素建立一个新的坐标系。

8.7. ‘preserveAspectRatio’ 属性

名称 初始值 可动画
preserveAspectRatio <align> <meetOrSlice>? xMidYMid meet
<align> =
    none
    | xMinYMin | xMidYMin | xMaxYMin
    | xMinYMid | xMidYMid | xMaxYMid
    | xMinYMax | xMidYMax | xMaxYMax
<meetOrSlice> = meet | slice

指示是否强制统一缩放。适用于所有建立新 SVG 视口的元素(参见 建立 SVG 视口的元素),以及 imagemarkerpatternview 元素。

在某些情况下(通常是使用 viewBox 时),希望图形以非统一的方式拉伸,以填满整个 SVG 视口;而在其他情况下,则希望使用统一缩放以保持图形的宽高比。

对于建立新 SVG 视口的元素(参见 建立 SVG 视口的元素),以及 markerpatternview 元素,只有当同一元素上同时提供了 viewBox 时,preserveAspectRatio 才生效;如果未提供 viewBox,则忽略 preserveAspectRatio

对于 image 元素,preserveAspectRatio 指示引用的图像应如何相对于参考矩形进行适配,以及是否应在当前用户坐标系中保持图像的宽高比。

<align> 参数指示是否强制统一缩放;若强制,则在 <align> 与 SVG 视口宽高比不匹配时决定对齐方式。<align> 必须是以下字符串之一:

<meetOrSlice> 参数是可选的。如果提供,它与 <align> 值之间由一个或多个空格隔开,且必须是以下字符串之一

PreserveAspectRatio 示例 展示了 preserveAspectRatio 的各种取值。该示例通过在 最外层 svg 元素 中嵌入多个 svg 子元素(参见 建立新的 SVG 视口)来创建若干新的 SVG 视口。

<svg width="450px" height="300px" xmlns="https://w3org.cn/2000/svg">

  <desc>Example PreserveAspectRatio - illustrates preserveAspectRatio attribute</desc>

  <style type="text/css">
    text { font-size: 9; }
    rect { fill: none; stroke: blue; }
  </style>

  <defs>
    <g id="smile">
      <rect x='.5' y='.5' width='29' height='39' style="fill:black;stroke:red"/>
      <circle cx='15' cy='20' r='10'  fill='yellow'/>
      <circle cx='12' cy='17' r='1.5' fill='black'/>
      <circle cx='17' cy='17' r='1.5' fill='black'/>
      <path d='M 10 24 A 8 8 0 0 0 20 24' stroke='black' stroke-width='2'/>
    </g>
  </defs>

  <rect x="1" y="1" width="448" height="298"/>

  <text x="10" y="30">SVG to fit</text>
  <g transform="translate(20,40)"><use href="#smile" /></g>

  <text x="10" y="110">Viewport 1</text>
  <g transform="translate(10,120)"><rect x='.5' y='.5' width='49' height='29'/></g>

  <text x="10" y="180">Viewport 2</text>
  <g transform="translate(20,190)"><rect x='.5' y='.5' width='29' height='59'/></g>

  <g id="meet-group-1" transform="translate(100, 60)">
    <text x="0" y="-30">--------------- meet ---------------</text>
    <g>
      <text y="-10">xMin*</text>
      <rect x='.5' y='.5' width='49' height='29'/>
      <svg preserveAspectRatio="xMinYMin meet" viewBox="0 0 30 40" width="50" height="30">
	<use href="#smile" />
      </svg>
    </g>
    <g transform="translate(70,0)">
      <text y="-10">xMid*</text>
      <rect x='.5' y='.5' width='49' height='29'/>
      <svg preserveAspectRatio="xMidYMid meet" viewBox="0 0 30 40" width="50" height="30">
	<use href="#smile" />
      </svg>
    </g>
    <g transform="translate(0,70)">
      <text y="-10">xMax*</text>
      <rect x='.5' y='.5' width='49' height='29'/>
      <svg preserveAspectRatio="xMaxYMax meet" viewBox="0 0 30 40" width="50" height="30">
        <use href="#smile" />
      </svg>
    </g>
  </g>

  <g id="meet-group-2" transform="translate(250, 60)">
    <text x="0" y="-30">---------- meet ----------</text>
    <g>
      <text y="-10">*YMin</text>
      <rect  x='.5' y='.5' width='29' height='59'/>
      <svg preserveAspectRatio="xMinYMin meet" viewBox="0 0 30 40" width="30" height="60">
	<use href="#smile" />
      </svg>
    </g>
    <g transform="translate(50, 0)">
      <text y="-10">*YMid</text>
      <rect x='.5' y='.5' width='29' height='59'/>
      <svg preserveAspectRatio="xMidYMid meet" viewBox="0 0 30 40" width="30" height="60">
	<use href="#smile" />
      </svg>
    </g>
    <g transform="translate(100, 0)">
      <text y="-10">*YMax</text>
      <rect x='.5' y='.5' width='29' height='59'/>
      <svg preserveAspectRatio="xMaxYMax meet" viewBox="0 0 30 40" width="30" height="60">
	<use href="#smile" />
      </svg>
    </g>
  </g>

  <g id="slice-group-1" transform="translate(100, 220)">
    <text x="0" y="-30">---------- slice ----------</text>
    <g>
      <text y="-10">xMin*</text>
      <rect x='.5' y='.5' width='29' height='59'/>
      <svg preserveAspectRatio="xMinYMin slice" viewBox="0 0 30 40" width="30" height="60">
	<use href="#smile" />
      </svg>
    </g>
    <g transform="translate(50,0)">
      <text y="-10">xMid*</text>
      <rect x='.5' y='.5' width='29' height='59'/>
      <svg preserveAspectRatio="xMidYMid slice" viewBox="0 0 30 40" width="30" height="60">
	<use href="#smile" />
      </svg>
    </g>
    <g transform="translate(100,0)">
      <text y="-10">xMax*</text>
      <rect x='.5' y='.5' width='29' height='59'/>
      <svg preserveAspectRatio="xMaxYMax slice" viewBox="0 0 30 40" width="30" height="60">
	<use href="#smile" />
      </svg>
    </g>
  </g>

  <g id="slice-group-2" transform="translate(250, 220)">
    <text x="0" y="-30">--------------- slice ---------------</text>
    <g>
      <text y="-10">*YMin</text>
      <rect x='.5' y='.5' width='49' height='29'/>
      <svg preserveAspectRatio="xMinYMin slice" viewBox="0 0 30 40"
           width="50" height="30">
	<use href="#smile" />
      </svg>
    </g>
    <g transform="translate(70,0)">
      <text y="-10">*YMid</text>
      <rect x='.5' y='.5' width='49' height='29'/>
      <svg preserveAspectRatio="xMidYMid slice" viewBox="0 0 30 40" width="50" height="30">
	<use href="#smile" />
      </svg>
    </g>
    <g transform="translate(140,0)">
      <text y="-10">*YMax</text>
      <rect x='.5' y='.5' width='49' height='29'/>
      <svg preserveAspectRatio="xMaxYMax slice" viewBox="0 0 30 40" width="50" height="30">
	<use href="#smile" />
      </svg>
    </g>
  </g>
</svg>
Example PreserveAspectRatio — demonstrate available options

Example PreserveAspectRatio

8.8. 建立新的 SVG 视口

在 SVG 内容中包含一个 svg 元素会创建一个新的 SVG 视口,所有包含的图形都在其中绘制;这隐式地建立了新的视口坐标系和新的用户坐标系。此外,由于已经建立了新的 SVG 视口,百分比单位在此处的含义也随之改变(参见 Units)。

新 SVG 视口的边界由建立该视口的元素(例如 svg)上的 ‘x’‘y’‘width’‘height’ 属性决定。新视口坐标系和新用户坐标系的原点均位于 (‘x’, ‘y’),其中 ‘x’‘y’ 为建立该 SVG 视口的元素的对应属性值。新视口坐标系和新用户坐标系的方向与建立该视口的元素当前的用户坐标系方向保持一致。新视口坐标系和新用户坐标系中的一个单位大小与建立该视口的元素当前用户坐标系中的一个单位大小相同。

这里是一个例子

<?xml version="1.0" standalone="no"?>
<svg width="4in" height="3in"
     xmlns="https://w3org.cn/2000/svg">
  <desc>This SVG drawing embeds another one,
    thus establishing a new SVG viewport
  </desc>
  <!-- The following statement establishing a new SVG viewport
       and renders SVG drawing B into that SVG viewport -->
  <svg x="25%" y="25%" width="50%" height="50%">
     <!-- drawing B goes here -->
  </svg>
</svg>

有关创建新 SVG 视口的详细示例,请参见 PreserveAspectRatio 示例

以下元素会建立新的 SVG 视口:

出于历史原因,patternmarker 元素即使接受 viewBox 也不创建新视口。clipPathmask 元素同样不创建新视口。这些元素内部的百分比长度并不与图形效果区域的尺寸成比例。

foreignObject 元素会为其子内容建立一个新的 CSS 包含块。同样,videoaudiocanvas 元素在渲染其后备内容时也会产生类似效果。要渲染位于 foreignObject 内的 SVG 元素,需要在该 svg 元素中再建立 SVG 文档片段和 SVG 视口。

一个 imageiframe 元素会为被引用的文档创建一个新的 文档视口。如果被引用的文档是 SVG 文件,则它自然会建立自己的 SVG 视口。

是否为新 SVG 视口额外建立剪裁路径取决于建立该视口的元素的 overflow 属性值。

8.9. 单位

SVG 对属性、表现属性以及 CSS 属性遵循 CSS 值与单位模块 [css-values] 中所描述的通用值和单位的定义。每个属性和属性必须声明其使用的组件值类型。后续或扩展的 CSS 工作组或 SVG 工作组的规范可能会扩展基本数据类型或添加新数据类型。

对于被定义为相对于 SVG 视口大小的 <percentage>

Example Units 展示了一些不同单位类型的处理规则。

<?xml version="1.0" standalone="no"?>
<svg width="400px" height="200px" viewBox="0 0 4000 2000"
     xmlns="https://w3org.cn/2000/svg">
  <title>Example Units</title>
  <desc>Illustrates various units options</desc>

  <!-- Frame the picture -->
  <rect x="5" y="5" width="3990" height="1990"
        fill="none" stroke="blue" stroke-width="10"/>

  <g fill="blue" stroke="red" font-family="Verdana" font-size="150">
    <!-- Absolute unit specifiers -->
    <g transform="translate(400,0)">
      <text x="-50" y="300" fill="black" stroke="none">Abs. units:</text>
      <rect x="0" y="400" width="4in" height="2in" stroke-width=".4in"/>
      <rect x="0" y="750" width="384" height="192" stroke-width="38.4"/>
      <g transform="scale(2)">
        <rect x="0" y="600" width="4in" height="2in" stroke-width=".4in"/>
      </g>
    </g>

    <!-- Relative unit specifiers -->
    <g transform="translate(1600,0)">
      <text x="-50" y="300" fill="black" stroke="none">Rel. units:</text>
      <rect x="0" y="400" width="2.5em" height="1.25em" stroke-width=".25em"/>
      <rect x="0" y="750" width="375" height="187.5" stroke-width="37.5"/>
      <g transform="scale(2)">
        <rect x="0" y="600" width="2.5em" height="1.25em" stroke-width=".25em"/>
      </g>
    </g>

    <!-- Percentages -->
    <g transform="translate(2800,0)">
      <text x="-50" y="300" fill="black" stroke="none">Percentages:</text>
      <rect x="0" y="400" width="10%" height="10%" stroke-width="1%"/>
      <rect x="0" y="750" width="400" height="200" stroke-width="31.62"/>
      <g transform="scale(2)">
        <rect x="0" y="600" width="10%" height="10%" stroke-width="1%"/>
      </g>
    </g>
  </g>
</svg>
Example Units — demonstrate available options

Example Units

左侧的三个矩形演示了使用绝对单位标识符 “in”(英寸)的情况。CSS 将 1 英寸定义为等于 96 像素。因此,使用英寸指定的最上方矩形的尺寸恰好与使用用户单位(每英寸对应 96 用户单位)指定的中间矩形的尺寸相同。底部矩形展示了当英寸单位的数值被缩放时会发生的情况。

中间的三个矩形演示了使用相对单位标识符 “em”。由于在最外层 g 元素上将 font-size 设置为 150,因此每个 “em” 单位等同于 150 用户单位。最上方的矩形使用 “em” 单位,其尺寸恰好与使用用户单位(每 “em” 对应 150 用户单位)指定的中间矩形相同。底部矩形展示了当 “em” 单位的数值被缩放时的结果。

右侧的三个矩形演示了使用百分比单位。请注意,针对 SVG 视口元素(在本例中为 最外层 svg 元素)的用户坐标系,其宽度与高度分别为 4000 与 2000,因为处理 viewBox 属性会导致用户坐标系被变换。最上方的矩形使用百分比单位,其尺寸恰好与使用等价用户单位指定的中间矩形相同。特别是,中间矩形的 stroke-width 属性被设置为 sqrt((actual-width)**2 + (actual-height)**2) / sqrt(2) 的 1 %,在本例中即 0.01 × sqrt(4000² + 2000²) / sqrt(2) ≈ 31.62。底部矩形展示了当百分比单位的数值被缩放时的效果。

8.10. 边界框

边界框 (bounding box)

元素的边界框(或称 “bbox”)是与该元素用户坐标系坐标轴对齐、能够完全包围该元素及其后代的最紧凑矩形。

可以为元素计算三种类型的边界框:

  1. The 对象边界框 是仅包含元素几何形状的边界框。对于 基本形状,它是被填充的区域。除非另有说明,否则未加限定的术语 “边界框” 指的就是此概念。
  2. The 描边边界框 是包含元素几何形状及其 描边形状 的边界框。
  3. The 装饰边界框 是包含元素几何形状、其 描边形状标记 的边界框。

请注意,元素上的 opacityvisibilityfillfill-opacityfill-rulestroke-dasharraystroke-dashoffset 属性的取值对元素的边界框没有影响。

对于曲线形状,边界框必须包围曲线边缘的所有部分,而不仅仅是端点。注意,曲线的控制点如果未定义为位于生成曲线的直线上(例如 Cubic Bézier 命令的第二组坐标),则这些控制点不应计入边界框的尺寸(虽然这些点可能位于边界框内部,如果它们位于形状内部,或位于曲线附近)。例如,位于曲线外侧、距离曲线边缘更远的控制点必须从边界框中排除。

Image showing the object bounding box of a quadratic Bézier curve.

The path 'M20,50 L35,100 H120 V50 Q70,10 20,50' 用淡蓝色显示。左侧显示了该路径的正确对象边界框。请注意,它不包括曲线的最高控制点,但它包含了全部蓝色形状,即使这些部分超出了控制点的凸包。

即使元素不在 渲染树 中——例如它被设置为 'display: none'、位于 defs 元素内部、通常不渲染的 symbol 元素或当前未出现在文档树中——它仍然拥有边界框。对该元素调用 getBBox 将返回与该元素已渲染时相同的矩形。然而,不在 渲染树 中的元素不会对任何祖先元素的边界框产生贡献。

以下示例定义了一系列元素。每个带有 ID 的元素预期的 对象边界框 如下所示。

<svg xmlns="https://w3org.cn/2000/svg">

  <title>Bounding Box Calculation</title>
  <desc>Examples of elements with different bounding box results based on context.</desc>

  <defs id="defs-1">
     <rect id="rect-1" x="20" y="20" width="40" height="40" fill="blue" />
  </defs>

  <g id="group-1">
    <use id="use-1" href="#rect-1" x="10" y="10" />

    <g id="group-2" display="none">
      <rect id="rect-2" x="10" y="10" width="100" height="100" fill="red" />
    </g>
  </g>
</svg>
元素 ID 边界框结果
"defs-1" {0, 0, 0, 0}
"rect-1" {20, 20, 40, 40}
"group-1" {30, 30, 40, 40}
"use-1" {30, 30, 40, 40}
"group-2" {10, 10, 100, 100}
"rect-2" {10, 10, 100, 100}

对于 文本内容元素,在计算边界框时,必须把每个字形视为独立的图形元素。计算时必须假设所有字形占据 完整字形单元格 完整字形单元格 的宽度等于水平前进距离,高度等于水平文本的 EM 框。对于横向排版的竖直文本,完整字形单元格 的宽度等于 EM 框,高度等于水平前进距离。对于其他竖直文本,完整字形单元格 的宽度等于 EM 框,高度等于竖直前进距离;如果字体未定义竖直前进距离,则高度等于 EM 框的高度。 例如,对于水平文本,计算时必须假设每个字形在垂直方向上延伸至字体的完整上升值和下降值。

由于声明式或脚本动画可以改变元素的形状、大小和位置,边界框是可变的。因此,边界框应反映请求该边界框时(无论是通过脚本调用还是作为声明式或链接语法的一部分)元素在特定时间点的当前值。

宽度、长度或两者均为零的元素(例如垂直或水平线,或宽度或高度为零的 rect 元素)仍然拥有边界框,如果指定了正的尺寸,则该维度取正值;如果未指定正尺寸,则宽度和高度均为 '0'。同样,path 元素的子路径段在宽度和高度为零的情况下,也必须计入该元素的几何以计算边界框。

未指定位置的元素(例如 path 元素的 d 属性值为 none)在计算边界框时视为位于点 (0,0)。

请注意,DOM 对象不继承自 SVGGraphicsElement(如渐变元素)的元素没有边界框,因此也没有用于请求边界框的接口。

即使 渲染树 中的元素引用了未解析的资源,它们仍然拥有边界框,由属性中指定的位置和尺寸决定;如果未提供属性值,则使用这些属性的 初始值。例如,元素 <use href="#bad" x="10" y="10"/> 将拥有 x、y 坐标均为 10、宽度和高度均为 0 的边界框。

下面的算法定义了如何为给定元素计算边界框。该算法的输入为:

根据 element 的类型,计算边界框的算法如下:

a 形状
a 文本内容元素
an a element within a 文本内容元素
  1. box 为矩形,初始为 (0, 0, 0, 0)。
  2. fill-shapeelement等价路径(若它是 形状),否则为包含该元素内部文本对应的每个字形单元格的形状。
  3. 如果 fill 为 true,则将 box 设置为坐标系 space 中能够完全包含 fill-shape 的最小矩形。

    fillfill-opacityfill-rule 属性的取值不会影响 fill-shape

  4. 如果 stroke 为 true 且元素的 stroke 不是 none,则将 box 设为 box 与坐标系 space 中能够完全包含该元素的 描边形状 的最小矩形的并集(假设该元素没有虚线模式)。

    stroke-opacitystroke-dasharraystroke-dashoffset 的取值不影响描边形状的计算。

  5. 如果 markers 为 true,则对元素上渲染的每个标记 marker
    1. 对于定义了 marker 内容的 marker 元素中的每个后代 图形元素 child
      1. 如果 child 在其所在的 marker 中拥有祖先元素的 'display: none'、其条件处理属性未通过,或不是 agsvgswitch 元素,则继续处理下一个后代 图形元素
      2. 否则,将 box 设为 box 与调用本算法对 child(元素为 child,目标坐标空间为 spacefillstrokemarkers 均为 true,clipped 传入 clipped)所得到的边界框的并集。
  6. 如果 clipped 为 true 且 elementclip-path 值不是 none,则将 box 设为坐标系 space 中能够包含 box 与裁剪路径交集的最小矩形。
  7. 返回 box
a 容器元素
use
  1. box 为矩形,初始为 (0, 0, 0, 0)。
  2. parent 为若是容器元素则为该容器元素,否则为 use 元素的影子树根。
  3. 对于 parent 的每个后代 图形元素 child
    1. 如果 child 未渲染,则继续下一个后代 图形元素
    2. 否则,将 box 设为 box 与调用本算法对 child(元素为 child)并使用与当前算法输入相同的 spacefillstrokemarkersclipped 值所得到的边界框的并集。
  4. 如果 clipped 为 true:
    • 如果 elementclip-path 值不是 none,则将 box 设为坐标系 space 中能够包含 box 与裁剪路径交集的最小矩形。
    • 如果 overflow 属性适用于 element 且其值不是 visible,则将 box 设为坐标系 space 中能够包含 box 与元素溢出边界的交集的最小矩形。
    • 如果 clip 属性适用于 element 且其值不是 auto,则将 box 设为坐标系 space 中能够包含 boxclip 属性所指定矩形的交集的最小矩形。
  5. 返回 box
canvas
foreignObject
iframe
image
video
  1. box 为坐标空间 space 中能够包含元素的 ‘x’‘y’‘width’‘height’ 几何属性所定义的 定位矩形 的最小矩形。

    本算法的 fillstrokemarkers 输入参数对这些元素返回的边界框没有影响。

  2. 如果 clipped 为 true 且 elementclip-path 值不是 none,则将 box 设为坐标系 space 中能够包含 box 与裁剪路径交集的最小矩形。
  3. 返回 box

值为 (0, 0, 0, 0) 且为空形状的并集 box 仍为 box

元素的 对象边界框描边边界框装饰边界框 是使用以下参数调用上述边界框计算算法的结果:element 为元素本身;space 为元素的用户坐标系统;fill 为 true;若计算 描边边界框装饰边界框stroke 为 true,否则为 false;若计算 装饰边界框markers 为 true,否则为 false;并且 clipped 为 false。

8.11. 对象边界框单位

以下元素可通过在给定元素上将指定属性设置为 'objectBoundingBox',以对象边界框的分数(在某些情况下也可为百分比)来表达坐标值和长度。

元素 属性 效果
linearGradient gradientUnits 表明用于指定渐变向量的属性(x1y1x2y2)表示所应用元素的边界框的分数或百分比。
radialGradient gradientUnits 表明用于指定中心(cxcy)、半径(r)以及焦点(fxfy)的属性表示所应用元素的边界框的分数或百分比。
pattern patternUnits 表明用于定义图案平铺方式的属性(xywidthheight)是使用所应用元素的边界框来建立的。
pattern patternContentUnits 表示图案内容的用户坐标系是使用应用该图案的元素的边界框建立的。
clipPath clipPathUnits 表明 clipPath 元素内容的用户坐标系统是使用所裁剪路径所作用的元素的边界框来建立的。
mask maskUnits 表明用于定义遮罩区域的属性(xywidthheight)是使用所应用遮罩的元素的边界框来建立的。
mask maskContentUnits 表明 mask 元素内容的用户坐标系统是使用所应用遮罩的元素的边界框来建立的。
filter filterUnits 表明用于定义 滤镜效果区域 的属性(xywidthheight)表示所应用滤镜的元素的边界框的分数或百分比。
filter primitiveUnits 表示滤镜原语内的各种长度值表示应用该滤镜的元素的边界框的分数或百分比。

在下文的讨论中,适用元素 指给定效果所作用的元素。对于渐变和图案,适用元素是其 fillstroke 属性引用该渐变或图案的 图形元素(有关 文本元素 的特殊规则,请参阅 对象边界框单位与文本元素 的讨论)。对于裁剪路径、遮罩和滤镜,适用元素可以是 容器元素图形元素

当使用关键字 objectBoundingBox 时,其效果就如同在嵌套变换矩阵列表中插入了一个补充变换矩阵,从而创建了一个新的用户坐标系。

首先,(minx,miny) 与 (maxx,maxy) 坐标由适用元素的 对象边界框 的范围决定。

然后,新用户坐标系中的坐标 (0,0) 被映射到适用元素用户坐标系内紧密边界框的 (minx,miny) 角,新用户坐标系中的坐标 (1,1) 被映射到适用元素紧密边界框的 (maxx,maxy) 角。在大多数情况下,以下变换矩阵会产生正确的效果

[ (maxx-minx) 0 0 (maxy-miny) minx miny ]

当在定义渐变向量、图案平铺、滤镜区域或遮罩区域的属性中使用百分比时,百分比值与相应的小数值等价(例如 50% 与 0.5 等价)。如果在 patternclipPathmaskfilter 元素的内容中使用百分比,这些值将按照 单位章节中定义的百分比处理规则进行处理。

对于表示为对象边界框单位的分数或百分比的值,可以指定任何数值。特别地,可以指定小于零或大于一的分数,以及小于 0% 或大于 100% 的百分比。

当适用元素的几何形状没有宽度或高度时(例如水平线或垂直线),不应使用关键字 objectBoundingBox,即使该线在查看时因非零描边宽度而具有实际厚度,因为边界框计算会忽略描边宽度。当适用元素的几何形状没有宽度或高度且指定了 objectBoundingBox 时,则给定的效果(例如渐变或滤镜)将被忽略。

8.12. SVG 内容的内在尺寸属性

为了在使用 CSS 格式化的宿主文档中包含 SVG,需要计算 具体对象尺寸。该 具体对象尺寸 必须使用 CSS Images 3 中定义的 默认尺寸算法(参见 css-images-3),并使用以下输入进行计算:

svg 元素的 widthheight 这两个尺寸属性的实际使用值确定 指定尺寸

同时,也必须从 widthheight 这两个尺寸属性中确定 内在尺寸。如果 widthheight 未被指定,则使用其初始值 'auto''auto' 与百分比长度不得用于确定 内在宽度内在高度

对于位图图像格式,内在尺寸固定在图像文件中,宿主文档可根据需要指定尺寸以缩放图像。SVG 本身是可伸缩的,它会将 内在宽度内在高度 适配为 指定尺寸 的宽度和高度。因此,当以长度形式指定时,svg 元素的 widthheight 尺寸属性控制 SVG 图像的 内在尺寸以及在宿主文档中放置 SVG 图像时使用的 指定尺寸

必须使用以下算法计算 内在宽高比。如果算法返回 null,则说明不存在内在宽高比。

  1. 如果 svg 元素上的 widthheight 尺寸属性均为绝对值
    1. return width / height
  2. 如果存在活动的 SVG 视图 (SVG View)
    1. let viewbox be the viewbox defined by the active SVG View
    2. return viewbox.width / viewbox.height
  3. 如果 svg 元素的 viewBox 正确指定
    1. let viewbox be the viewbox defined by the viewBox attribute on the svg element
    2. return viewbox.width / viewbox.height
  4. return null

本节定义的行为特定于 CSS,但可适配到其他宿主环境。在所有宿主环境中,内在宽高比(若可用)在为 SVG 视口设定尺寸时必须被尊重。

示例

示例: 固有长宽比 1
<svg xmlns="https://w3org.cn/2000/svg"
     width="10cm" height="5cm">
  ...
</svg>

在此示例中,SVG 视口的内在宽高比为 2:1。内在宽度为 10 cm,内在高度为 5 cm。

示例: 固有长宽比 2
<svg xmlns="https://w3org.cn/2000/svg"
     width="100%" height="50%" viewBox="0 0 200 200">
  ...
</svg>

在此示例中,最外层 SVG 视口的内在宽高比为 1:1。此情况下的宽高比计算允许在仅在一个方向受约束的包含块内以 object 方式嵌入。

示例: 固有长宽比 3
<svg xmlns="https://w3org.cn/2000/svg"
     width="10cm" viewBox="0 0 200 200">
  ...
</svg>

在这种情况下,固有长宽比为 1:1。

示例: 固有长宽比 4
<svg xmlns="https://w3org.cn/2000/svg"
     width="75%" height="10cm" viewBox="0 0 200 200">
  ...
</svg>

在此示例中,固有长宽比为 1:1。

为新出现的 auto 值添加更多示例?比如 David Vest 提供的示例

8.13. 向量效果

SVG 2 要求 SVG 2 将基于 SVG 1.2 Tiny 引入受约束的变换。
决议 向 SVG 2 规范中加入向量效果扩展提案。
目的 以包含非缩放特性(对象的非缩放部分,以及整个对象的非缩放)
所有者 Satoru Takagi (ACTION-3619)

有时希望对象的轮廓保持原始宽度,或对象的位置在任何变换下保持不变。例如,在地图中用 2 px 宽的线条表示道路时,即使用户放大地图,也希望道路保持 2 px 宽;又如在可平移的图表说明中也有类似需求。

为了提供关于特殊坐标变换和图形绘制的此类效果,SVG Tiny 1.2 引入了 vector-effect 属性。虽然 SVG Tiny 1.2 只引入了非缩放描边行为,但本版本加入了多种额外效果。而且这些效果可以组合指定,从而呈现更多样的视觉效果。未来的 SVG 语言版本也将通过该属性实现更强大的向量效果。

non-scaling-strokenone 之外的 vector-effect 取值因缺乏实现可能在 SVG 2 中被移除。请在实现阶段反馈实现这些取值的可行性。

名称 vector-effect
none | [ non-scaling-stroke | non-scaling-size | non-rotation | fixed-position ]+ [ viewport | screen ]?
初始值 none(无)
应用于 图形元素use
可继承
百分比 不适用
媒体 视觉
计算值 按指定值
可动画:
none(无)
指定不应应用矢量效果,即使用来自 SVG 1.1 的默认渲染行为,即首先用指定的涂料填充形状的几何形状,然后用指定的涂料描绘轮廓。
non-scaling-stroke
请参阅 此处 对绘制中向量效果的描述。
non-scaling-size
为此元素及其后代指定一种受约束的变换,以实现特殊的 用户坐标系统。该用户坐标系统的缩放量不随宿主坐标空间的 CTM 变化而变化,但并不抑制旋转或倾斜,也不固定用户坐标系统的位置。由于 non-scaling-size 抑制了用户坐标系统的缩放,它同时具有 non-scaling-stroke 的特性。变换公式与示例行为将在后续章节给出。
non-rotation
为此元素及其后代指定一种受约束的变换,以实现特殊的 用户坐标系统。该用户坐标系统的旋转与倾斜在宿主坐标空间的 CTM 变化时被抑制,但并不抑制缩放,也不固定其位置。变换公式与示例行为将在后续章节给出。
fixed-position
为此元素及其后代指定一种受约束的变换,以实现特殊的 用户坐标系统。该用户坐标系统的位置在宿主坐标空间的 CTM 变化时保持固定,但不抑制旋转、倾斜和缩放。当同时存在 transform 属性时,该属性的平移分量 ef 将用于移动固定的用户坐标系统原点。变换公式与示例行为将在后续章节给出。

这些取值可以枚举,从而可以同时指定具备这些特性的复合效果。

以下两个取值用于说明上述取值的宿主坐标空间,特别是在嵌套视口坐标系统(如嵌套内容或嵌套 svg 元素)中的元素时有效。若未指定,初始值为 viewport

视口
指定最近的 视口坐标系统 为宿主坐标空间。当该元素位于嵌套的 视口坐标系统 中时,向量效果将针对该元素直接所属的 视口坐标系统 起作用。即该向量效果不受祖先 CTM 变化的影响。
screen
它指定了受 用户代理 直接控制的内容的坐标系。可以说,它是 用户代理 所拥有的 “scrren”(在 SVGT1.2 中称为 “screen coordinate space”)。即使该元素属于嵌套视口坐标系,该矢量效果仍始终对任意层级的 CTM 变化产生影响。如果 SVG 实现是支持 CSS 兼容 px 单位的 用户代理 的一部分,则它是根层内容的 CSS 像素坐标系。通常情况下,设备的像素(或点)与 CSS 像素并不总是相等,这受到 用户代理 本身的缩放功能以及 DPI 变化的影响(参见 分辨率 [CSS Values and Units Module Level 3])。因此,该值并不限定转换必须相对于此类设备坐标系。

注意:SVG 的未来版本可能会提供指定设备坐标系的方法。

8.13.1. 计算矢量效果

本节列出了有关各种取值组合的变换公式列表,以阐明矢量效果的行为(不包括具有明确含义的 non-scaling-stroke)。

vector-effect 属性对在 3D 渲染上下文 中执行的变换没有任何影响。

用户坐标系视口坐标系 的普通坐标变换公式如下。

x 视口 y 视口 1 = CTM x 用户空间 y 用户空间 1 CTM = a ctm c ctm e ctm b ctm d ctm f ctm 0 0 1
<circle vector-effect="veValue" transform="translate(xo yo)" cx="xf" cy="yf" r=".."/>

vector-effect 如上所述添加到一个元素时,用户坐标到设备坐标的变换公式会改变如下。这里,xfyf 是相应元素及其后代的用户坐标;xoyo 是该元素拥有的 transform 属性中矩阵元素 ef。此外,|det(CTM)| 表示 CTM 行列式的绝对值。当该值为 0 且指定了 non-scaling-size 时,vector-effect 失效,即相当于 none

det CTM = a ctm d ctm - b ctm c ctm
veValue 公式
non-scaling-size
x 视口 y 视口 1 = CTM 0 0 1 + CTM det CTM 1 0 0 0 1 0 0 0 0 x f y f 1
non-rotation
x 视口 y 视口 1 = CTM 0 0 1 + det CTM 1 0 0 0 1 0 0 0 0 x f y f 1
non-scaling-size non-rotation
x 视口 y 视口 1 = CTM 0 0 1 + 1 0 0 0 1 0 0 0 0 x f y f 1
fixed-position
x 视口 y 视口 1 = x o y o 1 + CTM 1 0 0 0 1 0 0 0 0 x f y f 1
fixed-position non-scaling-size
x 视口 y 视口 1 = x o y o 1 + CTM det CTM 1 0 0 0 1 0 0 0 0 x f y f 1
fixed-position non-rotation
x 视口 y 视口 1 = x o y o 1 + det CTM 1 0 0 0 1 0 0 0 0 x f y f 1
fixed-position non-scaling-size non-rotation
x 视口 y 视口 1 = x o y o 1 + 1 0 0 0 1 0 0 0 0 x f y f 1

8.13.2. 计算嵌套视口坐标系的矢量效果

下面给出未使用矢量效果的嵌套视口坐标系的普通坐标变换公式。xviewport(UA)yviewport(UA) 是受 用户代理 直接控制的坐标。CTMthis 是从目标图形的 用户坐标系 到其所属的 视口坐标系 的变换矩阵。CTMparent 是上述 视口坐标系 到其父元素的 视口坐标系 的变换矩阵。CTMroot 则是根视口坐标系(即用户代理)的 CTM

x viewport(UA) y viewport(UA) 1 = CTM root ... CTM parent CTM this x 用户空间 y 用户空间 1

将前一节的七个公式应用于嵌套视口坐标系时,根据 vector-effect 的附加值是 viewport 还是 screen,公式的使用方式会相应改变。

当指定 viewport 值时,用户代理 计算坐标时会结合前一章的七个公式以及以下公式。

x 视口 ( UA ) y 视口 ( UA ) 1 = CTM root ... CTM parent x 视口 y 视口 1 CTM = CTM this

当指定 screen 值时,用户代理 计算坐标时会结合前一章的七个公式以及以下公式。

x 视口 ( UA ) y 视口 ( UA ) 1 = x 视口 y 视口 1 CTM = CTM root ... CTM parent CTM this

8.13.3. 矢量效果示例

下面示例展示了 non-scaling-stroke vector-effect 的效果。

<?xml version="1.0"?>
<svg xmlns="https://w3org.cn/2000/svg"
     width="6cm" height="4cm" viewBox="0 0 600 400"
     viewport-fill="rgb(255,150,200)">

  <desc>Example non-scaling stroke</desc>
  <rect x="1" y="1" width="598" height="398" fill="none" stroke="black"/>

  <g transform="scale(9,1)">
    <line stroke="black" stroke-width="5" x1="10" y1="50" x2="10" y2="350"/>
    <line vector-effect="non-scaling-stroke" stroke="black" stroke-width="5"
        x1="32" y1="50" x2="32" y2="350"/>
    <line vector-effect="none" stroke="black" stroke-width="5"
        x1="55" y1="50" x2="55" y2="350"/>
  </g>

</svg>

下面示例展示了 none vector-effect(即没有矢量效果)。

更改 CTM 之前更改 CTM 之后
Image showing none vector effect Image showing none vector effect

源代码

<svg xmlns="https://w3org.cn/2000/svg" viewBox="-50,-50,500,500" height="500" width="500">

  <rect x="-50" y="-50" width="500" height="500" stroke="orange" stroke-width="3" fill="none"/>

  <!-- Nested user coordinate system is transformed by this transform attribute -->
  <g transform="matrix(2.1169438081370817,0.3576047954311102,-0.3576047954311102,1.4700998667618626,0,0)
                translate(-50,-50)">
    <svg viewBox="-50,-50,500,500" height="500" width="500">
      <!-- Graph paper on the this svg's base user coordinate system -->
      <g stroke="green" stroke-width="1" fill="none">
        <circle cx="0" cy="0" r="10"/>
        <circle cx="150" cy="150" r="7"/>
        <path fill="green" stroke="none" d="M0,-3 L30,-3 25,-10 50,0 25,10 30,3 0,3z"/>

        <line x1="-100" y1="-100" x2="600" y2="-100" stroke-dasharray="5,5"/>
        <line x1="-100" y1="000" x2="600" y2="000"/>
        <line x1="-100" y1="100" x2="600" y2="100" stroke-dasharray="5,5"/>
        <line x1="-100" y1="200" x2="600" y2="200" stroke-dasharray="5,5"/>
        <line x1="-100" y1="300" x2="600" y2="300" stroke-dasharray="5,5"/>
        <line x1="-100" y1="400" x2="600" y2="400" stroke-dasharray="5,5"/>
        <line x1="-100" y1="500" x2="600" y2="500" stroke-dasharray="5,5"/>

        <line y1="-100" x1="-100" y2="600" x2="-100" stroke-dasharray="5,5"/>
        <line y1="-100" x1="000" y2="600" x2="000"/>
        <line y1="-100" x1="100" y2="600" x2="100" stroke-dasharray="5,5"/>
        <line y1="-100" x1="200" y2="600" x2="200" stroke-dasharray="5,5"/>
        <line y1="-100" x1="300" y2="600" x2="300" stroke-dasharray="5,5"/>
        <line y1="-100" x1="400" y2="600" x2="400" stroke-dasharray="5,5"/>
        <line y1="-100" x1="500" y2="600" x2="500" stroke-dasharray="5,5"/>
      </g>

      <!-- Figure having vector effect -->
      <!-- A thick red right arrow and small rectangle on this figure's nested
           user coordinate system origin -->
      <path id="ve" vector-effect="none"
            stroke="red" stroke-width="3" fill="none"
            transform="matrix(1,0,0,1,150,150)"
            d="M-50,-50 L50,-50 50,-100 150,0 50,100 50,50 -50,50 -50,-50z M5 0 L0 -5 -5 0 0 5z"/>
    </svg>
  </g>
</svg>

下面示例展示了 non-scaling-size 的效果。

更改 CTM 之前更改 CTM 之后
Image showing appearance before the effect of the vector effect showing up Image showing non-scaling-size vector effect
<path id="ve" vector-effect="non-scaling-size"
      stroke="red" stroke-width="3" fill="none"
      transform="matrix(1,0,0,1,150,150)"
      d="M-50,-50 L50,-50 50,-100 150,0 50,100 50,50 -50,50 -50,-50z M5 0 L0 -5 -5 0 0 5z"/>

下面示例展示了 non-rotation 的效果。

更改 CTM 之前更改 CTM 之后
Image showing appearance before the effect of the vector effect showing up Image showing non-rotation vector effect
<path id="ve" vector-effect="non-rotation"
      stroke="red" stroke-width="3" fill="none"
      transform="matrix(1,0,0,1,150,150)"
      d="M-50,-50 L50,-50 50,-100 150,0 50,100 50,50 -50,50 -50,-50z M5 0 L0 -5 -5 0 0 5z"/>

下面示例展示了 non-scaling-size non-rotation 的组合效果。

更改 CTM 之前更改 CTM 之后
Image showing appearance before the effect of the vector effect showing up Image showing non-scaling-size non-rotation vector effect
<path id="ve" vector-effect="non-scaling-size non-rotation"
      stroke="red" stroke-width="3" fill="none"
      transform="matrix(1,0,0,1,150,150)"
      d="M-50,-50 L50,-50 50,-100 150,0 50,100 50,50 -50,50 -50,-50z M5 0 L0 -5 -5 0 0 5z"/>

下面示例展示了 fixed-position 的效果。

更改 CTM 之前更改 CTM 之后
Image showing appearance before the effect of the vector effect showing up Image showing fixed-position vector effect
<path id="ve" vector-effect="fixed-position"
      stroke="red" stroke-width="3" fill="none"
      transform="matrix(1,0,0,1,150,150)"
      d="M-50,-50 L50,-50 50,-100 150,0 50,100 50,50 -50,50 -50,-50z M5 0 L0 -5 -5 0 0 5z"/>

下面示例展示了 non-scaling-size fixed-position 的组合效果。

更改 CTM 之前更改 CTM 之后
Image showing appearance before the effect of the vector effect showing up Image showing non-scaling-size fixed-position vector effect
<path id="ve" vector-effect="non-scaling-size fixed-position"
      stroke="red" stroke-width="3" fill="none"
      transform="matrix(1,0,0,1,150,150)"
      d="M-50,-50 L50,-50 50,-100 150,0 50,100 50,50 -50,50 -50,-50z M5 0 L0 -5 -5 0 0 5z"/>

下面示例展示了 non-rotation fixed-position 的组合效果。

更改 CTM 之前更改 CTM 之后
Image showing appearance before the effect of the vector effect showing up Image showing non-rotation fixed-position vector effect
<path id="ve" vector-effect="non-rotation fixed-position"
      stroke="red" stroke-width="3" fill="none"
      transform="matrix(1,0,0,1,150,150)"
      d="M-50,-50 L50,-50 50,-100 150,0 50,100 50,50 -50,50 -50,-50z M5 0 L0 -5 -5 0 0 5z"/>

下面示例展示了 non-scaling-size non-rotation fixed-position 的完整组合效果。

更改 CTM 之前更改 CTM 之后
Image showing appearance before the effect of the vector effect showing up Image showing non-scaling-size non-rotation fixed-position vector effect
<path id="ve" vector-effect="non-scaling-size non-rotation fixed-position"
      stroke="red" stroke-width="3" fill="none"
      transform="matrix(1,0,0,1,150,150)"
      d="M-50,-50 L50,-50 50,-100 150,0 50,100 50,50 -50,50 -50,-50z M5 0 L0 -5 -5 0 0 5z"/>

8.14. DOM 接口

8.14.1. 接口 SVGTransform

SVGTransform 接口用于表示出现在 transform 属性及其表现属性 ‘transform’gradientTransformpatternTransform 中的 <transform-function> 值。一个 SVGTransform 表示变换列表中的单个组件,例如单独的 scale(…)matrix(…) 值。

一个 SVGTransform 对象可以被指定为 只读,这意味着对该对象的任何修改尝试都会抛出异常,如下所述。

一个 SVGTransform 对象可以被 关联 到特定元素。关联的元素用于决定如果对象 反映 了该属性,则应更新哪个元素的 ‘transform’ 表现属性。除非另有说明,SVGTransform 对象默认不关联任何元素。

每个 SVGTransform 对象有两种工作模式之一。它可以

  1. 反映表现属性值的某个元素(通过 baseVal 成员在 SVGAnimatedTransformList 上公开),
  2. 保持分离,这种情况出现在使用 createSVGTransformcreateSVGTransformFromMatrix 创建的 SVGTransform 对象。

一个 SVGTransform 对象内部维护一个 <transform-function> 值,称为它的 value。它还维护一个 DOMMatrix 对象,称为它的 matrix object,该对象由 matrix IDL 属性返回。SVGTransform 对象的 matrix object 始终与它的 value 同步。

[Exposed=Window]
interface SVGTransform {

  // Transform Types
  const unsigned short SVG_TRANSFORM_UNKNOWN = 0;
  const unsigned short SVG_TRANSFORM_MATRIX = 1;
  const unsigned short SVG_TRANSFORM_TRANSLATE = 2;
  const unsigned short SVG_TRANSFORM_SCALE = 3;
  const unsigned short SVG_TRANSFORM_ROTATE = 4;
  const unsigned short SVG_TRANSFORM_SKEWX = 5;
  const unsigned short SVG_TRANSFORM_SKEWY = 6;

  readonly attribute unsigned short type;
  [SameObject] readonly attribute DOMMatrix matrix;
  readonly attribute float angle;

  void setMatrix(DOMMatrixReadOnly matrix);
  void setTranslate(float tx, float ty);
  void setScale(float sx, float sy);
  void setRotate(float angle, float cx, float cy);
  void setSkewX(float angle);
  void setSkewY(float angle);
};

SVGTransform 上定义的数值变换类型常量用于表示 SVGTransformvalue 的类型。其含义如下:

常量含义
SVG_TRANSFORM_MATRIX一个 matrix(…) 值。
SVG_TRANSFORM_TRANSLATE一个 translate(…) 值。
SVG_TRANSFORM_SCALE一个 scale(…) 值。
SVG_TRANSFORM_ROTATE一个 rotate(…) 值。
SVG_TRANSFORM_SKEWX一个 skewX(…) 值。
SVG_TRANSFORM_SKEWY一个 skewY(…) 值。
SVG_TRANSFORM_UNKNOWN其他类型的值。

使用数值变换类型常量是一种反模式,且不会为 SVGTransform 支持的任何变换类型引入新的常量值。如果支持并使用其他类型的变换,SVGTransform 将使用 SVG_TRANSFORM_UNKNOWN 类型。下面将说明在这些非标准变换类型下 SVGTransform 的其他属性是如何工作的。

type IDL 属性表示 SVGTransformvalue 所对应的变换项类型。获取 type 时,将执行以下步骤:

  1. 如果 SVGTransformvaluematrix(…)translate(…)scale(…)rotate(…)skewX(…)skewY(…) 之一,则返回上述变换类型表中的对应常量值。
  2. 否则,返回 SVG_TRANSFORM_UNKNOWN

    例如,对 scaleX(…)translate3d(…) 这类变换,将返回 SVG_TRANSFORM_UNKNOWN

matrix IDL 属性将变换表示为 4×4 齐次矩阵,获取时返回 SVGTransformmatrix object。当首次创建 matrix object 时,其数值会被设为匹配 SVGTransform 的变换函数 value,并设置为 反映 SVGTransform

请参阅 CSS Transforms 规范,了解不同变换函数类型如何对应特定矩阵值的描述。

angle IDL 属性表示 rotate(…)skewX(…)skewY(…) 变换函数的角度参数。获取时,将执行以下步骤:

  1. 如果 SVGTransform 对象的 valuerotate(…)skewX(…)skewY(…) 之一,则返回其角度参数(单位为度)。
  2. 否则,返回 0。

setMatrix 方法用于将 SVGTransform 设置为给定的矩阵值。当调用 setMatrix(matrix) 时,将执行以下步骤:

  1. 如果 SVGTransform 对象是 只读,则 抛出 NoModificationAllowedError
  2. 如果 matrixis2d 方法返回 true,则将 SVGTransform 对象的 value 设置为等价的 matrix(…) 值。
  3. 否则,将 SVGTransform 对象的 value 设置为等价的 matrix3d(…) 值。
  4. matrix 的矩阵分量复制到 SVGTransform 对象的 matrix object 中。
  5. 如果 SVGTransform 对象 反映了表现属性值的某个元素,则 重新序列化 该属性。

setTranslatesetScalesetRotatesetSkewXsetSkewY 方法用于将 SVGTransform 设置为新的变换函数值。调用其中任一方法时,将执行以下步骤:

  1. 如果 SVGTransform 对象是 只读,则 抛出 NoModificationAllowedError
  2. 根据被调用的方法,将 SVGTransform 对象的 value 设置为对应的新变换函数值。
    setTranslate(tx, ty)
    新变换函数值为 translate(tx, ty)
    setScale(sx, sy)
    新变换函数值为 scale(sx, sy)
    setRotate(angle, cx, cy)
    新变换函数值为 rotate(angle, cx, cy)
    setSkewX(angle)
    新变换函数值为 skewX(angle)
    setSkewY(angle)
    新变换函数值为 skewY(angle)
  3. SVGTransform 对象的 matrix object 的各分量设为匹配新的 value
  4. 如果 SVGTransform 对象 反映了表现属性值的某个元素,则 重新序列化 该属性。

本规范在 Geometry Interfaces 规范的基础上,对 DOMMatrix 对象的行为施加了额外要求,使其能够用于反映接受变换值的表现属性。

每个 DOMMatrix 对象同样有两种工作模式。它可以

  1. 反映一个 SVGTransform(通过 matrix IDL 属性在 SVGTransform 上公开),或
  2. 保持分离,这种情况出现在使用构造函数创建的 DOMMatrix 对象或通过 createSVGMatrix 创建的对象。

可以将 DOMMatrix 指定为 只读,这意味着对该对象的任何修改尝试都会抛出异常。当对只读 DOMMatrix 的任何 IDL 属性赋值,或调用其任何可变变换方法时,都会抛出 NoModificationAllowedError,而不是更新内部值。

请注意,此限制仅适用于可读写的 DOMMatrix 接口;未用于反映 transformDOMMatrixReadOnly 接口在尝试修改时已经会抛出异常。

当对可写的 DOMMatrix 的任何 IDL 属性赋值,或调用其任何可变变换方法后,在更新内部矩阵值后将执行以下步骤:

  1. 如果该 DOMMatrix 反映了一个 SVGTransform,则
    1. 如果该 DOMMatrixis2d 方法返回 true,则将对应的 SVGTransform 对象的 value 设置为等价的 matrix(…) 值。
    2. 否则,将该 SVGTransform 对象的 value 设置为等价的 matrix3d(…) 值。
    3. 如果 SVGTransform 对象 反映了表现属性值的某个元素,则 重新序列化 该属性。

8.14.2. 接口 SVGTransformList

SVGTransformList 接口是一个 列表接口,其元素为 SVGTransform 对象。SVGTransformList 表示 transform 属性可以取的值,即 <transform-list> 或关键字 none

[Exposed=Window]
interface SVGTransformList {

  readonly attribute unsigned long length;
  readonly attribute unsigned long numberOfItems;

  void clear();
  SVGTransform initialize(SVGTransform newItem);
  getter SVGTransform getItem(unsigned long index);
  SVGTransform insertItemBefore(SVGTransform newItem, unsigned long index);
  SVGTransform replaceItem(SVGTransform newItem, unsigned long index);
  SVGTransform removeItem(unsigned long index);
  SVGTransform appendItem(SVGTransform newItem);
  setter void (unsigned long index, SVGTransform newItem);

  // Additional methods not common to other list interfaces.
  SVGTransform createSVGTransformFromMatrix(DOMMatrixReadOnly matrix);
  SVGTransform? consolidate();
};

createSVGTransformFromMatrix 方法用于根据矩阵对象创建一个新的 SVGTransform 对象。当调用 createSVGTransformFromMatrix(matrix) 时,按如下步骤执行:

  1. transform 为新创建的、处于 分离 状态的 SVGTransform 对象。
  2. 随后执行与对 transform 调用 setMatrix 方法、并以 matrix 作为参数时相同的步骤。
  3. 返回 transform

consolidate 方法用于将变换列表合并为等价的单一变换函数。当调用 consolidate() 时,按如下步骤执行:

  1. 如果 SVGTransformList 对象是 只读,则 抛出 NoModificationAllowedError
  2. 如果列表为空,则返回 null。
  3. 分离 列表后移除其中的所有元素。
  4. transform 为新创建的 SVGTransform 对象。
  5. matrix 为先取单位矩阵,然后按顺序后乘列表中每个 SVGTransformmatrix object 的值得到的矩阵。
  6. transformmatrix object 的各分量设为 matrix 中的对应值。
  7. 如果 transformmatrix objectis2d 方法返回 true,则将 transformvalue 设置为等价的 matrix(…) 值。
  8. 否则,将 transformvalue 设置为等价的 matrix3d(…) 值。
  9. 附加 transform 到此 SVGTransformList
  10. transform 追加到该列表。
  11. 如果列表 反映 了某个表现属性,则 重新序列化 该属性。
  12. 返回 transform

所有其他 SVGLengthList 接口成员的行为在 列表接口 中有定义。

8.14.3. 接口 SVGAnimatedTransformList

一个 SVGAnimatedTransformList 对象用于 反映 transform 属性以及其对应的表现属性(根据元素不同,可能是 ‘transform’gradientTransformpatternTransform)。

[Exposed=Window]
interface SVGAnimatedTransformList {
  [SameObject] readonly attribute SVGTransformList baseVal;
  [SameObject] readonly attribute SVGTransformList animVal;
};

baseValanimVal IDL 属性表示已反映表现属性的值。获取 baseValanimVal 时,会返回一个 SVGTransformList 对象,它反映相应的表现属性。

8.14.4. 接口 SVGPreserveAspectRatio

SVGPreserveAspectRatio 接口用于表示 preserveAspectRatio 属性的取值。

一个 SVGPreserveAspectRatio 对象可以被指定为 只读,这意味着对该对象的任何修改尝试都会抛出异常,如下所述。

每个 SVGPreserveAspectRatio 对象 反映了基本值,即一个 已反映preserveAspectRatio 属性(通过 baseValanimVal 成员在 SVGAnimatedPreserveAspectRatio 上公开)。

[Exposed=Window]
interface SVGPreserveAspectRatio {

  // Alignment Types
  const unsigned short SVG_PRESERVEASPECTRATIO_UNKNOWN = 0;
  const unsigned short SVG_PRESERVEASPECTRATIO_NONE = 1;
  const unsigned short SVG_PRESERVEASPECTRATIO_XMINYMIN = 2;
  const unsigned short SVG_PRESERVEASPECTRATIO_XMIDYMIN = 3;
  const unsigned short SVG_PRESERVEASPECTRATIO_XMAXYMIN = 4;
  const unsigned short SVG_PRESERVEASPECTRATIO_XMINYMID = 5;
  const unsigned short SVG_PRESERVEASPECTRATIO_XMIDYMID = 6;
  const unsigned short SVG_PRESERVEASPECTRATIO_XMAXYMID = 7;
  const unsigned short SVG_PRESERVEASPECTRATIO_XMINYMAX = 8;
  const unsigned short SVG_PRESERVEASPECTRATIO_XMIDYMAX = 9;
  const unsigned short SVG_PRESERVEASPECTRATIO_XMAXYMAX = 10;

  // Meet-or-slice Types
  const unsigned short SVG_MEETORSLICE_UNKNOWN = 0;
  const unsigned short SVG_MEETORSLICE_MEET = 1;
  const unsigned short SVG_MEETORSLICE_SLICE = 2;

  attribute unsigned short align;
  attribute unsigned short meetOrSlice;
};

SVGPreserveAspectRatio 上定义的数值对齐类型常量用于表示 preserveAspectRatio 所能取的对齐关键字值。其含义如下:

常量含义
SVG_PRESERVEASPECTRATIO_NONE关键字 none
SVG_PRESERVEASPECTRATIO_XMINYMIN关键字 xMinYMin
SVG_PRESERVEASPECTRATIO_XMIDYMIN关键字 xMidYMin
SVG_PRESERVEASPECTRATIO_XMAXYMIN关键字 xMaxYMin
SVG_PRESERVEASPECTRATIO_XMINYMID关键字 xMinYMid
SVG_PRESERVEASPECTRATIO_XMIDYMID关键字 xMidYMid
SVG_PRESERVEASPECTRATIO_XMAXYMID关键字 xMaxYMid
SVG_PRESERVEASPECTRATIO_XMINYMAX关键字 xMinYMax
SVG_PRESERVEASPECTRATIO_XMIDYMAX关键字 xMidYMax
SVG_PRESERVEASPECTRATIO_XMAXYMAX关键字 xMaxYMax
SVG_PRESERVEASPECTRATIO_UNKNOWN其他类型的值。

同样,SVGPreserveAspectRatio 上定义的数值 meet-or-slice 类型常量用于表示 preserveAspectRatio 可以取的 meet-or-slice 关键字值。其含义如下:

常量含义
SVG_MEETORSLICE_MEET关键字 meet
SVG_MEETORSLICE_SLICE关键字 slice
SVG_MEETORSLICE_UNKNOWN其他类型的值。

align IDL 属性表示 preserveAspectRatio 值中的对齐关键字部分。获取时,执行以下步骤:

  1. value反映基本值preserveAspectRatio 属性值。若属性不存在或无效,则使用属性的 初始值
  2. 返回上表中对应于 value 中对齐关键字的常量值。

在设置 align 时,执行以下步骤:

  1. 如果该 SVGPreserveAspectRatio只读,则 抛出 NoModificationAllowedError
  2. 如果 valueSVG_PRESERVEASPECTRATIO_UNKNOWN 或在上表中没有对应的对齐关键字条目,则直接返回。
  3. string 为上表中对应于 value 的关键字。
  4. string 末尾追加单个 U+0020 空格字符。
  5. meet or slice 为本 SVGPreserveAspectRatiomeetOrSlice 成员返回的值。
  6. string 末尾追加对应于 meet or slice 的 meet-or-slice 关键字。
  7. 将已反映的 preserveAspectRatio 属性设为 string

meetOrSlice IDL 属性表示 preserveAspectRatio 值中的 meet-or-slice 关键字部分。获取时,执行以下步骤:

  1. valuepreserveAspectRatio 的当前非动画值(即反映基本值的属性值)。
  2. 如果 value 中未出现 meet-or-slice 关键字,则返回 SVG_MEETORSLICE_MEET
  3. 否则,返回上表中对应于 value 中 meet-or-slice 关键字的常量值。

在设置 meetOrSlice 时,执行以下步骤:

  1. 如果该 SVGPreserveAspectRatio只读,则 抛出 NoModificationAllowedError
  2. 如果 valueSVG_MEETORSLICE_UNKNOWN 或在上表中没有对应条目,则直接返回。
  3. align 为本 SVGPreserveAspectRatioalign 成员返回的值。
  4. string 为对齐关键字表中对应于 align 的关键字。
  5. string 末尾追加单个 U+0020 空格字符。
  6. string 末尾追加对应于 value 的 meet-or-slice 关键字。
  7. 将已反映的 preserveAspectRatio 属性设为 string

8.14.5. 接口 SVGAnimatedPreserveAspectRatio

一个 SVGAnimatedPreserveAspectRatio 对象用于 反映 preserveAspectRatio 属性。

[Exposed=Window]
interface SVGAnimatedPreserveAspectRatio {
  [SameObject] readonly attribute SVGPreserveAspectRatio baseVal;
  [SameObject] readonly attribute SVGPreserveAspectRatio animVal;
};

The baseValanimVal IDL 属性表示已反映的 preserveAspectRatio 属性的当前非动画值。获取 baseValanimVal 时,会返回一个 SVGPreserveAspectRatio 对象,该对象 反映基值(即 preserveAspectRatio 属性)位于获取该对象的 SVG 元素上,而该元素的反映 IDL 属性类型为 SVGAnimatedPreserveAspectRatio