Skip to content
0

SVG

SVG 是一种 XML 语言,类似于 XHTML,可以用来绘制矢量图形

Look

下面是用 SVG 技术画的一个简单的图形,包括了圆形、矩形和文本元素

Open

资源引用

svg 可以嵌入到 HTML 当中,就像上面的 plaground 中的例子,直接把 SVG 标签嵌入到 HTML 标签中

TIP

在允许的情况下,这其实是最好的使用方式,既在文档中保留了 SVG 的 DOM 结构,又和其他元素在同一个上下文中

当我们的 SVG 资源是一个网络资源的时候,我们可以使用 <img>、<object>、<iframe> 标签引用 SVG 资源:

<img src="/logo.svg" />
<object data="/logo.svg" type="image/svg+xml" />
<iframe src="/logo.svg" />

这三种方式产生的结果有些不一样,当使用 img 标签渲染 SVG,浏览器会将 SVG 当作图像处理,而不是解析其中的 DOM 结构,这意味着无法获取 SVG 中 DOM 结构,自然也通过 CSS 选择器改变样式

三种渲染方式的产物

所以我们可以选择 object 或者 iframe 标签来渲染,二者工作的方式类似:浏览器会将 SVG 当作一个独立的文档来处理,所以我们就可以看到结果

下面就是三者在浏览器中渲染出的 DOM 结构示意:

<img src="/logo.svg" />
<object data="/logo.svg" type="image/svg+xml">
  #document
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" viewBox="0 0 512 512" xml:space="preserve">
    ...
    </svg>
</object>
<iframe src="/logo.svg">
  #document
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" viewBox="0 0 512 512" xml:space="preserve">
      ...
    </svg>
</iframe>

命名空间

您可以注意到上面的 SVG 标签中,带有 xmlnsxmlns:xlink 等属性,这是和 SVG 命名空间有关的部分

SVG 是一种 XML 语言,或者说说 XML 的一种方言,W3C 的长期目标是使不同类型的基于 XML 的内容能够在同一个 XML 文件中混合使用

方言

XHTMLRSSMathML 等,它们都是 XML 的方言。当处于同一个 XML 中的时候,因为它们各自有着自己的规则。很容易存在解析冲突等问题,比如在 XHTML 和 SVG 都有一个 <title>标签,解析器不知道应该以 XHTML 还是 SVG 的规范来解析它

因此:需要通过给标签显式的「命名空间声明」告诉解析器它属于哪个方言

<svg xmlns="http://www.w3.org/2000/svg">
  <rect />
  <circle />
</svg>

之所以叫做命名空间是因为 xmlns 确实创建了一个空间的概念,也就是说,上面 SVG 中的 rect 和 circle 标签都会以 http://www.w3.org/2000/svg 规范来解析

我们最常见和常用的空间就是 http://www.w3.org/2000/svg,看起来它是一个 URI,所以常常被称作 namespace URI,但是实际不承担链接跳转的任务,仅仅作为一个字符串标识符。

空间覆盖

空间之内还可以创建其他的空间,例如下面的 SVG 将会使用指定的新的命名空间

<html xmlns="http://www.w3.org/1999/xhtml">
  <body>
    <!-- some XHTML tags here -->
    <svg xmlns="http://www.w3.org/2000/svg" width="300px" height="200px">
    <!-- some SVG tags here -->
    </svg>
  <!-- some XHTML tags here -->
  </body>
</html>

xlink

用于描述 XML 与 XML 或其他文档之间的链接。它的一些行为留给“方言”实现来确定如何处理,已经不推荐使用了

XLink 提供了一种在 XML 文档中创建链接的标准方式,这也包括了在 SVG 中的应用,常常在 SVG 标签上声明 xmlns:xlink 属性

xlink 有几个属性,过去常用的属性是 xlink:href,用于指定元素的超链接目标

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" height="100">
  <!-- 创建超链接到外部网页 -->
  <a xlink:href="https://github.com/peterroe">
    <rect width="100" height="100" fill="#D39C6D" />
  </a>

  <!-- 创建超链接到另一个 SVG 文件 -->
  <a xlink:href="/logo.svg" target="_blank">
    <circle cx="50" cy="50" r="40" fill="#664D44" />
  </a>
</svg>
点矩形和圆跳转不同的链接:

现代 SVG 和 HTML5 已经支持超链接属性 href 而无需特定的命名空间,但在一些较旧的文档和实现中,你可能仍然会看到使用 xlink:href 的方式。但是在现代的文档中,你可以直接使用 href 属性而不添加 xlink 命名空间声明

Element

SVG 元素的默认宽高是 300 和 150,下面是一个例子(样式写法基于 UnoCss

<svg bg-red></svg>

下面介绍一些 SVG 中的基本的元素

TIP

在例子中,为方便观察,右侧渲染的 SVG 标签会有一个默认的边框和固定 150 和 100 的宽高,在左侧的示例代码中不会展示,CSS 如下:

svg {
  border: 1px solid var(--vp-c-brand-1);
  width: 150px;
  height: 100px;
}

circle

属性功能
cx圆心横坐标(相对左上角)
cy圆心纵坐标(相对左上角)
r半径
fill圆填充的颜色(默认是黑色)
stroke圆框的颜色
stroke-width圆框的宽度
<svg>
  <circle cx="40" cy="40" r="30" fill="#AE9EE8" />
</svg>

rect

属性功能
x左上角点的横坐标(相对左上角)
y左上角点的纵坐标(相对左上角)
height高度
width宽度
rx矩形的水平角半径
ry矩形的垂直角半径,默认和 rx 相同
fill填充的颜色
<svg>
  <rect
    x="10" y="10" width="80" height="60" rx="10"
    fill="skyblue"
  />
</svg>

line

属性功能
x1第一个点的横坐标
y1第一个点的纵坐标
x2第二个点的横坐标
y2第二个点的纵坐标
stroke线的颜色
<svg>
  <line
    x1="10" y1="10" 
    x2="60" y2="70" stroke="gray"
  />
</svg>

g

g 元素意为 group,用于包裹其他 SVG 元素,以便赋予相同的属性

<svg>
  <g fill="white" stroke="green" stroke-width="5">
    <circle cx="40" cy="40" r="25" />
    <circle cx="60" cy="60" r="25" />
  </g>
</svg>

path

path 元素是定义形状的通用元素,所有基本形状都可以使用 path 元素创建。同时,它的规则也非常的复杂

属性 d 用于描述路径,内置了多种命令来辅助我们绘制路径

TIP

path 中的,只是为了提升可读性,并无实质格式要求,不影响最终结果

命令含义参数描述
MMoveToX Y移动当前点位置到(X,Y)
LLineToX Y当前点位置向(X,Y)连直线
HHorizontal LineToX当前点画一条水平线到 x 坐标为 X
VVertical LineToY当前点画一条垂直线到 x 坐标为 Y
CCurveToC1x C1y, C2x C2y,X Y以(C1x,C1y)和(C2x,C2y)为控制点,画一条曲线到(X,Y)
QQuadratic Bezier CurveToCx Cy, X Y以 Cx 和 Cy 为控制点,画一条曲线到(X,Y)
AElliptical Arcrx ry, xar, laf, sf, x, yrx半长轴的长度,ry半短轴的长度,xar旋转角度,laf大弧标志,sf扫描标志,(x,y)终点
ZClosePath结束 path,连接起点,从而形成闭环路径,便于填充颜色
<svg>
  <path stroke="blue" fill="white" d="
    M10 20
    L30 40
    H 50
    V 50
    C80 50, 50 80, 80, 80
    Q80 50, 100 50
    A30 40, 0, 0, 0, 140, 20
    Z
  "></path>
</svg>
undefined
undefined

对三次贝塞尔曲线不了解的可以跳转这个 playground,它有两个控制轴。

二次贝塞尔曲类似,和三次的区别就是只有一个控制轴

ViewBox

这个属性可以截取 SVG 中某个区域直接放大显示

举个例子,有一个长款为 100 的方形 SVG,其内部也有一个方形 rect 元素

<svg width="100" height="100">
  <rect
    x="10" y="10" width="30" height="30"
    fill="skyblue"
  />
</svg>

通过 viewBox 属性,我们可以让放大 rect 这一块区域,充满整个 SVG:

<!-- 设置 viewBox 的区域就是 rect 的区域 -->
<svg
  width="100" height="100" viewBox="10 10 30 30"
>
  <rect
    x="10" y="10" width="30" height="30"
    fill="skyblue"
  />
</svg>

当然,上面的情况是 viewBox 的比例和 SVG 的完全比例一样的理想情况。在实际情况中,很有可能它们的比例是不一样的,因此我们需要去定义 viewBox 放大后,如何去适应 SVG 的区域

我们通过 preserveAspectRatio 属性去定义,它的值由空格分隔的两个值组合而成,例如:

<svg preserveAspectRatio="xMidYMid meet"></svg>
<svg preserveAspectRatio="xMaxYMid slice"></svg>

第一个值表示,viewBox 如何与 SVG viewport 对齐;第二个值表示,如何维持高宽比(如果有)

其中,第1个值又是由两部分组成的:

含义
xMinviewport 和 viewBox 左边对齐
xMidviewport 和 viewBox x轴中心对齐
xMaxviewport 和 viewBox 右边对齐
YMinviewport 和 viewBox 上边缘对齐。注意Y是大写。
YMidviewport 和 viewBox y轴中心点对齐。注意Y是大写。
YMaxviewport 和 viewBox 下边缘对齐。注意Y是大写。

第二部分的值支持下面三个:

含义
meet保持纵横比缩放 viewBox 适应 viewport
slice保持纵横比同时比例小的方向放大填满 viewport
none扭曲纵横比以充分适应 viewport

我们直接上示例:

<svg width="300" height="150">
  <circle
    cx="40" cy="40" r="20"
    fill="skyblue"
  />
</svg>

为了便于观察,我们再次将 viewBox 设定为 rect 的区域:

<svg width="300" height="150" viewBox="20 20 40 40">
  <circle
    cx="40" cy="40" r="20"
    fill="skyblue"
  />
</svg>

此时我们看到

  • viewBox 的比例是 40/40 = 1
  • SVG viewport 的比例是 300/150 = 3/2

viewBox 和 SVG viewport 的比例不一致,而 preserveAspectRatio 的默认值是 xMidYMid meet

所以我们看到的效果是横向和竖向居中,保持 viewBox 的纵横比缩放适应 viewport

我们再来看看 preserveAspectRatio 其他值的效果

<!-- xMin 效果:viewport和viewBox左边对齐 -->
<svg
  width="300" height="150" viewBox="20 20 40 40"
  preserveAspectRatio="xMinYMid meet"
>
  <circle
    cx="40" cy="40" r="20"
    fill="skyblue"
  />
</svg>
<!-- meet 效果:缩放viewBox适应viewport(默认) -->
<svg
  width="300" height="150" viewBox="40 40 70 20"
  preserveAspectRatio="xMidYMid meet"
>
  <rect
    x="40" y="40" width="70" height="20"
    fill="skyblue"
  />
</svg>
<!-- slice 效果:向比例小的方向放大 -->
<svg 
  width="300" height="150" viewBox="40 40 70 20"
  preserveAspectRatio="xMidYMid slice"
>
  <rect
    x="40" y="40" width="70" height="20"
    fill="skyblue"
  />
</svg>
<!-- none 效果:拉伸 viewBox 填满
  此时 xMidYMin 已无效果,所以不需要写 -->
<svg 
  width="300" height="150" viewBox="40 40 70 20"
  preserveAspectRatio="none"
>
  <rect
    x="40" y="40" width="70" height="20"
    fill="skyblue"
  />
</svg>

Released under the MIT License.